A Java program using JavaFX that provides a game authoring environment allowing game designers, people with no programming skills, to build turn-based strategy games, using a variety of visual tools and requiring minimal programming.
Names:
- Vincent Liu (jl729)
- Haotian Wang (hw186)
- Amy Kim (yk154)
- Natalie Le (nl121)
- Jonathan Nakagawa (jyn2)
- Inchan Hwang (ih33)
- Jason Zhou (jz192)
- Yunhao Qing (yq50)
Start Date: 10/30/2018
Finish Date: 12/9/2018
Hours Spent: ~800
- Authoring Environment
- Vincent Liu : Frontend developer. Created basic front-end layout; Created basic graph with drag and drop functionality; Worked on save and load function for phase graph; Enabled tile generation in the front end;
- Haotian Wang : Frontend developer. Created the frameworks for front-end and for middle-end integration with back-end. Worked on (de)-serialization, event handling, data passing, convenient features and error handling.
- Amy Kim : Frontend developer. Worked on phase pane; Enabled players, properties, sound, grid layout; Worked on GUI and convenient features.
- Game Engine
- Natalie Le : Backend engine developer. Created the engine/gameplay infrastructure and the social center extension.
- Jonathan Nakagawa : Frontend engine developer.
- Authoring Engine
- Inchan Hwang : Backend authoring engine developer. Worked on the Groovy and Phase part of the authoring engine, game engine, save/load/export from authoring engine, overall integration.
- Jason Zhou : Backend authoring engine developer. Worked on GameObjectCRUD and various specific game objects such as Entity, Tile and Sound.
- Yunhao Qing : Backend authoring engine developer. Worked on GameObjeectCRUD and Player component.
- checkbackground.jpg
- RockPaperScissors_Background.jpg
- TicTacToe_Background.png
- background.png
- ocean.jpeg
- sunset.jpg
- duke_logo.png
- paper.png
- person_logo.png
- rock.png
- scissor.png
- image_not_found.png
- Initialize a static final field in the constructor
- Integer.class vs int.class
- How to permanently remove few commits from remote branch
- Split() String method in Java with examples
- Is there a way to instantiate a class by name in Java?
- How to convert Double to int directly?
- How do I split a string with multiple spaces?
- Oracle documentation on Observable
- Oracle documentation on Shape
- Oracle documentation on InvocationTargetException
- Oracle documentation on JavaFx Color
- The Java ternary operator examples
- JavaFX Dialogs
- How to Handle Jave Exception
- Designing with Interfaces
- Designing with Exceptions
- OO in one Sentence
- Oracle documentation on Circle
- Java: Self for static method calls within the same class
- Calling a subclass method from superclass
- Is there a way to instantiate a class by name in Java?
- Oracle documentation on Class
- Initialize a static final field in the constructor
- Integer.class vs int.class
- How to permanently remove few commits from remote branch
- Split() String method in Java with examples
- Is there a way to instantiate a class by name in Java?
- How to convert Double to int directly?
- Oracle documentation on Random
- Location-Independent Access to Resources.
- How do I invoke a Java method when given the method name as a string?
- The Java ternary operator examples
- Line Chart Documentation
- Line Chart Live Updates
- Authoring: src/frontend/src/authoringInterface/MainAuthoringProgram.java
- Engine: src/controller/src/launching/GameLauncher.java
- VM Configuration:
--add-opens java.base/java.lang.reflect=xstream --add-opens java.base/java.util=xstream --add-opens java.base/java.text=xstream --add-opens java.desktop/java.awt.font=xstream --add-opens javafx.base/com.sun.javafx.collections=xstream --add-opens javafx.base/javafx.beans.binding=xstream --add-opens javafx.base/javafx.beans.property=xstream
- src/authoring_backend/test/CRUDTest.java : This file tests whether the game object CRUD is functioning correctly.
- src/authoring_backend/test/GroovyBlockTest.java : This file tests whether the groove components, such as the graph and nodes are working appropriately and whether grooveblock is functioning correctly.
- src/authoring_backend/test/PhaseSerializationTest.java : This file tests whether serialization of phase is successful.
- src/authoring_backend/test/SerializationTest.java : This file tests whether serialization of groove is successful with the game sword and arrow.
- src/authoring_backend/test/ValidationTest.java : This file tests whether validation is running.
- src/authoring_backend/test/XStreamTest.java : This file tests the XStream.
- src/frontend/test/GroovyShellTest.java : This class tests the functionality of the Groovy Shell.
- src/frontend/test/ImageSelectorTest.java : This class tests wheteher image selector is working.
- src/frontend/test/SerializationTest.java : This class tests the serialization of CRUD interface.
- src/frontend/test/SwitchingPaneTest.java : This class tests whether switching pane is successful.
- src/frontend/test/TestGameData.java : This class tests game data.
- Authoring Utils
- src/authoring_backend/src/authoringUtils/exception/DuplicateGameObjectClassException.java : This class handles the exception of having duplicate game object class in the CRUD.
- src/authoring_backend/src/authoringUtils/exception/DuplicateIdException.java : This class handles the exception of duplicating id being used in the CRUD.
- src/authoring_backend/src/authoringUtils/exception/GameObjectClassException.java : This class is extended by specific game object class exception.
- src/authoring_backend/src/authoringUtils/exception/GameObjectClassNotFoundException.java : This file handles the exception of calling a game object class that is not found.
- src/authoring_backend/src/authoringUtils/exception/GameObjectInstanceException.java : This class is extended by specific game object instance exception.
- src/authoring_backend/src/authoringUtils/exception/GameObjectInstanceNotFoundException.java : : This file handles the exception of calling a game object instance that is not found.
- src/authoring_backend/src/authoringUtils/exception/GameObjectTypeException.java : This class hanldes exception of game object type conflict.
- src/authoring_backend/src/authoringUtils/exception/IdException.java : This class is extended by specific id class exception.
- src/authoring_backend/src/authoringUtils/exception/InvalidGameObjectClassException.java : This file handles invalid game object class exception.
- src/authoring_backend/src/authoringUtils/exception/InvalidGameObjectInstanceException.java : This file handles invalid game object instance exception.
- src/authoring_backend/src/authoringUtils/exception/InvalidIdException.java : This class handles invalid id exceptions.
- src/authoring_backend/src/authoringUtils/exception/InvalidOperationException.java : This file handles invalid operations on id in CRUD.
- src/authoring_backend/src/authoringUtils/exception/InvalidPointsException.java : This class handles invalid point exception in CRUD.
- src/authoring_backend/src/authoringUtils/exception/TurnNotFoundException.java : This class handles the exception of a certain turn not found in CRUD.
- Groovy
- src/authoring_backend/src/groovy/graph/blocks/core/ArgNumberMismatchException.java : This class handles argument number mismatch exception.
- src/authoring_backend/src/groovy/graph/blocks/small_factory/ListParseException.java : This class handles list parse exception.
- src/authoring_backend/src/groovy/graph/blocks/small_factory/MapParseException.java : This class handles all kinds of map parse exception.
- src/authoring_backend/src/groovy/graph/blocks/small_factory/NoSuchPropertyException.java : This class handles no such property exception.
- src/authoring_backend/src/groovy/graph/blocks/small_factory/ReferenceParseException.java : This class handles reference parse exception.
- src/authoring_backend/src/groovy/graph/PortAlreadyFilledException.java : This class handles port already filled exception.
- src/authoring_backend/src/groovy/graph/PortNotConnectedException.java : This class handles port not connected exception.
- src/authoring_backend/src/phase/NamespaceException.java : This class handles name space exceptions.
- src/controller/src/social/UserException.java
- src/frontend/src/authoringInterface/sidebar/RedundantNamingException.java : This file handles duplicate naming exception.
- src/frontend/src/authoringInterface/subEditors/exception/IllegalGameObjectNamingException.java : This file handles invalid naming for game objects exception.
- src/frontend/src/authoringInterface/subEditors/exception/IllegalGeometryException.java : This file handles illegal geometry exception.
- src/frontend/src/authoringInterface/subEditors/exception/MissingEditorForTypeException.java : This file handles missing editor for type exception.
- src/frontend/src/runningGame/IllegalSavedGameException.java : This class handles inappropriate saved game exception.
- src/frontend/src/utils/exception/GridIndexOutOfBoundsException.java : This class handles grid index out of bound exception.
- src/frontend/src/utils/exception/NodeNotFoundException.java : This class handles a specific node is not found exception.
- src/frontend/src/utils/exception/PreviewUnavailableException.java : This class handles preview unavailable excepton.
- src/frontend/src/utils/exception/UnhandledCoordinatesClassException.java : This class handles coordinates class unhandled exception.
- src/frontend/src/utils/exception/UnremovableNodeException.java : This class handles a certain node is unremovable exception.
- src/frontend/src/utils/exception/XMLParsingException.java : This class handles any issue in paring the XML.
The user can to customize images for entity, tile and user avatars. The user need to prepare these images and set the appropriate image for respective image objects in building the game.
Authoring Environment: Run the MainAuthoringProgram.java to access the authoring program.
Engine: Run the GameLauncher.java to access the game launcher. Click any game's "Play" button the play the game. Sign in to access social center functionality under the "Social" tag.
Anything else?
- Loading player image in the game.
- Redo and Undo are too difficult with our current framework and we did not manage to finish it before the deadline. However, basic framework (Memento Pattern) is set up in our project
- Initially we were unsure about how to build the authoring engine so the author can build a game with little code. We carefully studied the game TicTacToe and came up with the idea of having a grid and various game objects such as entities and tiles. We later decided to also have players, turns and categories as game objects too. After making the TicTacToe works, we started to think about how to make other games work. Inchan came up with the idea of Groovy, through which all kinds of logic can be converted from graphics such as nodes and lines to codes that can be run. These two components form the foundation of the authoring backend.
-
User can connect to social media such as Twitter and share result of playing the game instantly.
-
User can customize background music for the game and also customize sound effect on actions in playing the game.
-
User has a social center where he or she can see all the games he or she has created.
-
Convenient features; Batch mode and detachable tabs.
-
User can save and load the game graphically on the authoring engine; Groovy and Phase nodes are also saved and loaded.
-
Vincent Liu : VoogaSalad is more challenging compared to the previous 3 projects, Breakout, Cell Society and SLogo. I learnt how to work with a team by using interfaces and I truly feel I can work better in a team setting in the future.
-
Haotian Wang : I have been working on connecting backend and the UI. I enjoyed the process a lot, it is so fun to see how much changes I made and how flexible our codes become after I re-construct the structure of our project. I feel I understand the 'Open For Extension,Closed for Modification' much more after this project.
-
Amy Kim : This was a very big project; I had to debug and test a lot for implementing new features, even it was not a complicated feature. I have been working on the GUI and I really like the experience. Learning how all the UI components work, how the UI components are connected to the back-end and learning how to handle user input were challenging but fun as well.
-
Natalie Le : I had a really great time working on this project. I learned a lot about module dependencies, external libraries, and design patterns like Pub-Sub. My team and I also learned a lot about technical collaboration and best practices for engineering groupwork. It was awesome working with such great teammates!
-
Jonathan Nakagawa : I am truly happy with working on the sole of our program - the game engine frontend. I spent some time on UI as well. I have learnt a lot and I believe the skills and friendship I obtained from the project will last.
-
Inchan Hwang : This project is very challenging but I learnt so much about APIs and Reflection and I truly appreciate the power of JavaFX now! Although it is time-consuming, the project is definitely worth doing. I realise how much I enjoy backend coding and I aspire to be a backend engineer in the future!
-
Jason Zhou : The project is challenging in its concept and design. I learn a lot about APIs, Lambda, and Reflection. I had a great time design and implementing the front end and also learn how to connect the front end and the back end, which is very valuable for me.
-
Yunhao Qing : Building Voogasalad is very time-consuming. I get much more used to using Git as a part of a team through doing this project. I am responsible for authoring engine backend coding and I am very satisfied with working on backend.I learn about how flexibility of codes is important as making changes on flexible codes is much easier and changing codes is what we do all the time.
- Goblin vs troll game (Inchan)
- TicTacToe (Natalie)
- Connect4 (Yunhao)
- Reversi (Vincent)
- Battleship (Haotian)
- Wack-a-mole (Amy)
- Minesweeper (Jingchao)
To author a game, you must first define
- Grid layout
- Players
- Entities
- Tiles
- BGM (optional)
- Phase
- Win Condition
We're going to create a goblin vs troll game, with a simple goal of eliminating either the king goblin or the queen troll.
We define the grid size as 10x10.
It's a simple game, and players don't really have special statistics. So we just create two empty players A and B. You don't really have to add images.
The sidebar should be pre-initialized with some basic entities/tiles. Let's delete them. You can edit/delte each item on each category by right-clicking them (if it doesn't immediately pop-up, keep trying).
Be sure to save, and also to make backup versions in case something goes wrong. It's on the menubar File -> Save
.
After deleting everything, let's create some entities. We'll need
- Goblins: swordman, bowman, king
- Trolls: swordman, shaman, queen
First, we prepare some images for the entities.
#Notice how there are multiple images for each health point. These can be dynamically set through image selector
which we'll see soon.
#Note that transparent pixels are transparent to mouse clicks as well
Now we're ready to make entities on our authoring environment. Right click Entity
from sideview, and press add entry
. Fill in the fields EXCEPT for players/image selector/properties, as they'll throw some weird exceptions if you try to set them at this stage.
The goblin king can't attack so it has range 0.
# To remove an image from this window, click the image you want to remove and press delete
key from the keyboard.
We now right-click on the created entity from sideview and press edit to add properties and their default values.
As always, save and make backups as you go.
But we want to display the image that corresponds to the goblins's health. To do so, click on Image Selector
button.
#Be sure to do this AFTER creating an entity, and on the editing window.
So this is the GroovyPane
on which we need to construct some groovy code out of. In an image selector
, we need to return an integer index between 0
and # of images - 1
. Then, each goblin instance will select its image depending on the output. By default, the image selector
is constructed to return 0.
Also, since this is an image selector
, we can use $this
function from GameMethods category to refer to each instances that this image selector will take effect on.
Our logic will look something like
#Try to think of it as one of the LISP languages; It'll help. When you're stuck with implementing some logic, message Inchan for help...
Anyways, just exit out of the window when you're done, and press apply on the dialog.
#Even if you cancel from the dialog, your changes on the GroovyPanes will still be there unlike other elements
We repeat this process for all six entity types.
Note that nothing stops you from adding any other properties
Now, we're ready to define tiles!
It's very similar to how you'd define entities; But be aware of the fact that if you don't define tiles on a grid cell, any clicks on that grid cell will be ignored by the game engine.
Anyways, we only have one tile for this game.
You can define properties/Image selector on tiles too
Since more than 3 enemies can die on a tile, we have to modify our image selector a bit.
#Unlike Java, $return does NOT terminate the progression of the code; the last $return call will determine the final return value.
We place the tiles by dragging them onto the grid. You can press SHIFT and drag around the grid to generate multiple tiles at once! But becareful since it'll place multiple tiles if you go through that grid multiple times.
Now, we place entities onto the grid as well. You can use the batch placement functionality on entities as well.
On this step, you'll define what clicks and keypresses mean to your game.
We model "moves" made by players as FSMs. In this game, the "states" are,
a: Beginning of the turn. b: Selected one of my entity. c: Attacked an entity. d: Healed an ally. e: Moved to an empty tile. f: Replicated goblin king. g: Placed the replicated goblin king.
, and at c,d,e,g, the turn goes to the other player, starting at state a.
The transitions are,
a -> b: clicking current player's entity b -> a: pressing ESC b -> c: clicking an enemy entity within attackRange b -> d: (Only when selected = Troll Shaman) clicking an ally entity within healRange b -> e: clicking a empty tile within moveRange b -> f: (Only when selected = Goblin King) pressing SPACE f -> a: pressing ESC f -> g: pressing on a adjacent tile.
So, we create this phase diagram.
And on each transition, we can specify whether this transition should be taken or not. For example, for a->b, there's two conditions.
- The clicked object is an entity.
- The clicked entity should be the currentPlayer's entity.
on b->c, there are three conditions:
- The clicked object is an entity.
- The clicked entity should be other player's entity.
- The clicked entity should be within selected entity's attack range.
To set these transition conditions, we double click on the edge, and "write" scripts on the groovyPane. These should return a boolean value.
#The $clicked block represents the last entity/tile that was clicked.
The nodes also contain a script that gets executed when the game transitions to that node. For example, at state b, we must declare a variable selected
as the clicked entity. Variables can be declared with $
block on the literal section. All variables are global!
b->c: 3 conditions as described above
state c: Notice how we have to call toNextPlayer() to pass the turn. To take the game back to state a, we call goto("a")
b->d: clicking current player's entity within heal_range
state d: Making sure the hp doesn't go beyond max_hp.
state b->e: clicking on empty tile within move_range
You can sneak in a $print block to debug things
To test what we have until now, we disconnect b->f, and hit Run.
Movement works! But why is shaman's hp 1 from the start? Yeah the ordering was fucked; image selector outputs hp-1 but these images have hps 2 and 1.
Ok we now implement Goblin King's replication.
b->f: check if the selected entity has name Goblin_king
On f, we have nothing to do.
state g: creating another instance of goblin king at the clicked tile
Now, let's go ahead and test it.
To specify the ending condition, go to the win condition
tab. This script gets executed after every nodes' execution script.
This game ends when
- all goblin kings die
- the troll queen dies
Simple script to determine the winner
You can set the background music for the game by clicking Settings > BGM
on the menubar.
- Changing properties on the sideview will not (sometimes?) alter properties of things out on the grid.
- The initial order of the players are... randomly intialized. So try to make a symmetric game.
- Have AT LEAST one item on each ENTITY/TILE/PLAYER category before saving; If you don't have one, the whole category will disappear.
You can pretty much define your own function blocks if you need them; Just add another method on engine.gameplay.GameMethods
. The authoring environment will automatically generate the blocks. This is what I did to create block on the winning condition.
So, in a sense, you can literally write methods that represent image selector
s, guard
s, execution
s, winning conditions
s.