A multiplayer kart game in which two players compete to see who completes the entire track first
Warning: There are some flickerings due to the fast frames updates.
I have slowed it down quite a bit as discussed in Technical Notes.
Below are screenshots of the three scenes/pages involved:
Kart selection scene | Racing scene |
---|---|
After race scene | |
- Netbeans IDE 8.2
- JDK 1.8
Note: Player 1 = P1 | Player 2 = P2
P1 Keys | P2 Keys | Description |
---|---|---|
W | ⬆ | FORWARD |
A | ⬅ | ROTATE LEFT by 22.5° |
D | ➡ | ROTATE RIGHT by 22.5° |
S | ⬇ | BACKWARD |
- Kart will collide or crash when moving outside the race track or into the grassy area
- Kart's speed will decrease or accelerate over time.
Note: The speed-meters will show the current speed of both players
It decreases when there is no further forward or backward/reverse motion - Race ends when both karts are unable to continue (collided/ completed race). The following are such scenarios or conditions:
- Both karts collided
- Both karts crossed the starting/finish line
- One kart crossed the starting/finish line whereas another collided
- Create a project
- Include the source files inside the
src
directory - Start the server first by running ServerMain ⚙
- Next, start the client by running Main 🕹
- Repeat Step 4 to open another client window (Now, you should have 2 client windows - For player 1 and 2)
- Select a kart (Once both players have selected, you will be redirected to the race track) 🏎
- Race! 🏁🏁
- When the race ends, you will be redirected to the kart selection screen. You may race again by starting from Step 1! 🥳
- Race only starts when both clients have selected their respective karts
- A delay for when the application exits is caused by waiting for server's permission
- Only exit the application by pressing on the X button on the top-right of the application
(Doing otherwise will prevent the server from clearing relevant data appropriately)
And now, the fun part...🤣
Flickering is an issue related to animation. One common way of solving this is by applying double buffering. This method eliminates (or at least, in my case, reduces a significant amount of flickers) by drawing all the Animated elements into a buffer before performing only one graphics operation (instead of one for each element) on the screen. Find out more about Double Buffering
The implementation of this strategy inside the update()
function found on line 43
here:
Graphics gScene = scene.getGraphics();
if(gScene != null){
Graphics gBuffer = bufferedImage.createGraphics();
// Paint into buffer
for(Animated animated: animatedElements){
if(animated.getIsPaint()){
animated.paint(null, gBuffer); //Painting to buffer
}
if(timer.isRunning()){
animated.updateFrame();
}
}
gScene.drawImage(bufferedImage, 0, 0, scene); //Painting to screen
scene.paintComponent(gBuffer); //Update screen
}
In my implementation, server handles the tedious task of updating every player's state/kart's data (position, speed, etc.). The idea was taken from Gabriel's Client-Server Game Architecture (The explanations in there are amazing!). However, not everything was implemented (such as client-side prediction 😞) due to insufficient time assigned for this project.
Notice that on [Line 28 in Scene class], the constructor accepts params
or parameters. As someone who enjoys the amazing development experience provided by React, I thought of bringing props
to my applications. This allows different Scenes
to pass data to each other easily. The SceneManager is also a Singleton as only one SceneManager
is used to manage the scene/page navigation. When a navigation is detected, SceneManager
will alert Main
to replace the current Scene
with the new Scene
: Updating scene in Main
I had some beginner's experiences in using Unity. This game engine makes game development extremely convenient with its multitude of features. One of which is Collider
. Wrapping a component with Collider
will allow it to collide with other components with a Collider
wrapper. Similarly, the Collider class is used by classes which need a collider such as Kart and sides of the race track:
Note: The red rectangles are the colliders (Kart's collider is made smaller to avoid collision due to scratches)
The protocol here follows loosely on how API works.
Hence, you might find some similarities such as the message's purpose and status codes.
There are several payload types. Each indicates a different purpose of a message/response. The PayloadType
enum can be found here
Type |
---|
KART_SELECTED |
KART_UPDATE |
KART_EXIT |
CLIENT_EXIT |
The format of a client's message is:
PAYLOAD TYPE
DATA
Here is an example of a KART_UPDATE
request/message:
KART_UPDATE
Action:MOVE_FORWARD; Angle:36.0;
Note: The angle value is supplied because the images are loaded on client-side. Next, the number of frames/images are not fixed but are instead determined by the number of images inside the related folder. This allows for fewer hardcoded numbers and a more flexible Animated class.
See Kart A's frames and Animated class
The client exchanges messages with server in a certain order and format. These status codes returned alongside the server's responses will show if the previous client's messages are valid or not (and due to what reason if they aren't). The Status
enum can be found here
Status | Code | Text | Description |
---|---|---|---|
OK | 200 | OK | Request has been successfully processed |
INVALID_DATA | 400 | Invalid Data Syntax | Request contains wrongly formatted data |
INVALID_TYPE | 404 | Invalid Payload Type | Server does not recognize nor handle this payload type |
Here is an example of a KART_UPDATE
response:
200/KART_UPDATE
User:You; Name:kartA; Action:MOVE_FORWARD; X:525.0; Y:500.0; Speed:0.0;
User:Enemy; Name:kartB; Action:MOVE_BACKWARD; X:800.0; Y:550.0; Speed:0.0;
Note: The information within will be extracted upon arrival at client
Learn more about the extraction