This tutorial contains the basic guidelines to make a multiplayer game. As an example, we will use the game Desert Attack, which is a simple team deathmatch FPS. You may create an arena inside that project in here and take a look on most of the implementation of the game. All of the code can be found inside the arena (you can see the scripts and and change them), except for a script that creates all the objects in the arena so you start with an arena ready to play! Desert Attack also uses Presences, which are special objects used to represent the players inside the game, but all the code regarding characters is placed inside the object Character. You will find more information about the implementation of the game Desert Attack and how to change it here. The next image shows the object structure of Desert Attack:
Please read the tutorial Making a Simple Game for other guidelines for creating a game in ludiloom.
Ludiloom handles the distribution and synchronization required for multi-player games, but there are aspects in the development of these games that one must pay attention to. This tutorial explains the how multi-player games are developed in ludiloom.
Contents |
Setting a new arena
You may create a Desert Attack arena. Most of the game elements are created automatically through a onLoad script in the RuleBook. You can't see it from your arena, but it's there. You will find the full script inside the zip file. It creates the objects GameController, Scores, Logger, Weapons and Hud if they don't exist, like this:
GameController = Arena:getObjectsByTemplate("GameController")[1]; if not GameController then GameController = Arena:createObject("GameController","GameController",""); end
These important objects are assigned to global variables because they are used very often throughout the game. The same principle is applied in the creation of the cameras:
-- create spectator cam if not Arena:hasCamera("spectatorCam") then spectatorCam = Arena:createCamera("spectatorCam"); --cam:setSmoothEnabled(true); spectatorCam:setPosition(1000,0,1000); (...) spectatorCam:setLimits(1000,4000,1000,4000); else spectatorCam = Arena:getCamera("spectatorCam"); end if not Arena:hasCamera("playerCam") then playerCam = Arena:createCamera("playerCam"); (...) else playerCam = Arena:getCamera("playerCam"); end
It also removes that may be present in the arena when the player connects:
-- deactivate listeners Arena:removeKeyPressedListener("\200"); Arena:removeKeyReleasedListener("\200"); (...) Arena:removeMouseButtonPressedListener(MOUSE_BUTTON_LEFT); Arena:removeMouseButtonReleasedListener(MOUSE_BUTTON_LEFT);
And creates some listeners that have to be installed in the arena:
--create some listeners local im = InterModel.newIM(); Arena:setKeyPressedListener("\2",GameController,"toggleOptions",im,false,false,false,false,"1");
New arenas are created with a terrain and other set elements along with the object Set.
The Set object template has a preconfigured light (sun) and an initialization system. When the object is instantiated for the first time, it will run the onLoad script. The script loads a terrain from a Media Item. The terrain was edited using the ludiloom Terrain Editor, and saved to a file. Then it was uploaded back to the system as a Media Item. This is feature is pretty cool since it avoids long initialization scripts and provide better re-usability of terrains. See the page How To Edit The Terrain. The onLoad script does other things to compose the game environment. It sets the lighting, shadows and initializes de skybox:
System.print("Set onLoad"); Set = this; if this:getAttribute("initialized") == "1" then System.print("Set already initialized"); return; end -- activate terrain!! Terrain.loadFromMediaItem(MediaItem.newMI("0000000000000C21")); Arena:setLightColour(0,0,0); Arena.setGravity(-200); Arena.setShadowTechnique(3); Arena.setShadowTextureSize(1024); Arena.setShadowTextureCount(1); Arena.setShadowFarDistance(2000); Arena:setSkyBoxEnabled(true); Arena:setSkyBox("0000000000000259","0000000000000240","000000000000027F","000000000000026B","00000000000002AE","00000000000002C7",2000); this:setAttribute("initialized",1);
More about editing the game environment here.
Creating a multiplayer game
Creating a multiplayer game in ludiloom requires some special care and knowledge about how the game simulation and distribution works in ludiloom. Check this page for more details on distributed programming in ludiloom.
Distributed simulation
Ludiloom uses a distributed simulation model. Every computer connected to an arena runs a complete simulation whatever happens inside that arena. When we talk about “a simulation” we mean an instance of the game simulation model running in a ludiloom client application. If there are several players connected to the same arena, we say that there are several simulations. The information is shared between the several simulations through a transparent but pretty smart peer-to-peer network.
The principle of the distributed simulation is that if all simulations start with the same data model and they receive the same events by the same order, then the simulations will be “eventually synchronized”. The simulations in the several computers may not share the same precise state at all time, but virtually they are synchronized and consistent. For instance, in a FPS like Desert Attack, most of the information is synchronized (3D environment, player position, scores, etc.), but the HUD, camera position and user controls are different from player to player. The programmer of the game is responsible for controlling which information should be shared or not. Further in this page you will see the methods used for creating multi-player games in ludiloom. This model proved to work good enough even in fast-paced games such as Desert Attack.
Programming in a distributed environment
The ludiloom platform takes care of the distribution and persistence of the game, but the creator must understand the basic concepts of ludiloom's distribution model in order to achieve perfect synchronization network performance.
The question comes down to determine "who" executes "what"? In the tutorial Desert Attack you can see these principles applied to a multi-player FPS game.
Global/local listeners
The core state of the game should be consistent in the several running simulations, but there are always differences between the several simulations and game interfaces. For instance, a player may well be seeing the Scores panel while the other players don't. Also, the fact that she is seeing the Scores panel is not relevant to the overall game state, so the other simulations don't need to be notified of the event that executes the interaction to open the Scores panel. In this situation, the listener that triggers the event should be local. The first thing to ask when creating a listener is to ask the question: do other simulations need this event? If they don't, set the listener to local by unchecking the “global” checkbox in the graphical interface:
or, when creating listeners through the scripting language, set the last boolean of the function to false, like this:
Arena:setKeyPressedListener("\15",Scores,"showHideScore",im,false,false,false,false,"2");
If a listener isn't local, then it's global. The events triggered by it will be propagated through the network to the other simulations.
System.isLocalEvent()
One listener triggers one event that executes one interaction, you can't have two listeners for the same event nor execute two interactions with the same event. Sometimes it's required that the interaction executes in a different way when the event comes from the local simulation, that is, in the same "computer" where the event was triggered. For example, when the player presses the “Play” button in Desert Attack, it will an event that must be propagated to every simulation in connected to the arena because it triggers the interaction “createPlayer” in the object “GameController” which creates a new Presence and calls other interactions to update the Scores table, etc.. Every simulation must “know” that a player was created. On the local simulation, however, we want to do everything that is executed on the other simulations, plus hiding the Options panel. This should happen only in the computer where the player pressed the “Play” button. To do this we use a special api call that returns whether the event was generated locally or not – the System.isLocalEvent(). In the interaction “createPlayer” mentioned as example, we did:
if System.isLocalEvent() then this:getOverlay("options_overlay"):setVisible(false); Scores:getOverlay("score_panel"):setVisible(false); end
Object Update
The update mode can be set in the graphical interface:
Or through the ludiloom api function setPersistent. When the update mode is activated, it means that the global events will update the simulation model on the servers, thus assuring that the changes in the objects are persistent even after all players disconnect from the arena. If an object is not in update mode, then all the objects inside it will not be updated too. An object is only updated if all its parents up to the Arena object are in update mode.
Whenever possible the update mode should be turned off to eliminate unnecessary overhead on the network and servers.
You should also note that even if you are changing an object in edit mode (add models, edit a script, etc.) the object will not be updated and you'll loose the changes when you disconnect from the arena. When you turn the update mode on, the object synchronizes with the version on the server and all the changes you made before will persist in the server. As said before, the first simulation that connects to an arena receives a copy of the simulation model from the server. After that, other simulations connecting to the same arena will receive the Data Model from another peer simulation. You have to take care of reseting the game state of the new player: clean the interface, show the startup screen, reset variables, etc. The best practice is to create an on load listener and interaction so that each Object "knows" how to reset itself. The tutorial Desert Attack shows examples of using this feature correctly.
Presences
Presences are special objects used to represent the players in the game. They behave exactly like other objects except that they are associated to a user connected in the Arena. Learn more about Presences and how to create them here.
Presences help maintaining the consistency of the game simulation regarding players and their representations in-game – the avatars. Besides useful functions like getPresences, there are other essential features connected to Presences: the variables that and MyPresence.
The that is a special variable that is present in every interaction script and doesn't need to be explicitly declared or initialized. It is a reference to the presence that originated the event. When a player that has a Presence inside the arena triggers an event, the variable that in the interaction script will point to that presence.
The MyPresence is another special variable that always points to the presence associated to the local user, i.e., the presence associated with the user where the simulation is running. This means that the MyPresence variable points to a different Presence in each simulation.
To be sure that a presence is initialized properly in your game, you have to do something like this:
if MyPresence:isDefault() then nick = MyPresence:getName(); System.requestLoadPresence("DAPlayer",true); (...) end
In Desert Attack, this code is inside the onLoad script in the object GameController. It assures that the player will always be associated it a DAPlayer presence. DAPlayer is a Presence Template that created in the RuleBook (you can't see it nor change it).
Creating a character
Check out this HowTo and learn to make a FPS Character. Then, you will need the shooting tutorial and congratulations, you made yourself a game!





