SmartfoxCheckers
From WikiFlashed
Checkers is a fun simple two player game. ( http://www.playjava.com/images/board.gif ) A while ago when I was digging into SmartFoxServer, I thought, this game would be relatively easy as a learning curve game.
It was developed in AS3 with some 'hacky' parts to it. But it works quite well. I am not going to host a live sample since I can't afford a Server license yet. Maybe some time in the future when a good projects comes along with some investors.
Anyways read on. I have made this code FFA (Free For All).
Contents |
Author
- (c) Wikiflashed
- http://www.wikiflashed.com
- Free for All
Planning
A multiplayer game should be designed with a suitable programming pattern. Not only will this pattern save you time in the long run, it will also give it extendability. For checkers, I used the proxy based command pattern. At the time, this pattern sounded like the best approach for a turn based game.
Proxy pattern basically means that each node has a logic controller class called the "Referee" that talks to other nodes's referee instances and the referees themselves keep the state object synced. Players then interact with their own referee instance instead of talking directly to each other. This makes it easier to avoid having a server, client type of restriction on the in-game communication. There are probably better ways of doing this game but, this was a good learning experience for me.
I started off pretty much creating the entity classes: the referee, player, board, and the view, and continued from here.
For the pattern we need some interface files.
- IGameView - The interface for the visuals of the game (usually the core)
- AbstractReferee - The interface for referee
- ISymPlayer - The interface for proxy player
- SymPlayer - The actual player proxy class
- Game - The game view, in this game I pretty much merged all visuals and interaction things into one class (being lazy)
- CheckersReferee - The referee for checkers game. In another game, you could call this for example ChessReferee. Referee should handle the state maintenance of the game as well as the winning conditions. This is why its called the "Referee".
Problems
The hardest thing wasn't so much the game engine but the multiplayer lobby, join game, create game functionalities. SmartFoxServer back then had strange room count broadcast behaviors that it was just really hard to handle the problems when someone suddenly leaves in between the different user flows of the game.
Other than that, I had to also make a offline mode of the game where the player play against the computer. Using the proxy pattern, it was pretty easy since, one referee can have multiple proxy players underneath.
The lobby is a wrapper that loads the engine and I will add it to another page a bit later when I have some time. The SmartFoxServer events with sending and receiving serialized objects will be illustrated there.
Strange/Stupid SmartFoxServer Things
- Room count not always correct
- Players do not show up correctly when joining the lobby/game room, etc.
Main Class
The Main class is the initializer of everything. This class had to communicate with an external interface that sends and receives SmartFoxServer objects. Key functions are:
- onSendGameObject - Local user makes a move, the move needs to be sent to everyone (SmartFoxServer)
- onGameObject - Remote user made a move (onSendGameObject), and received by local user.
package Checkers { /** * Main * Just a simple initializer to test the game in Single Player Mode or Multiplayer Mode * @author Wiki Flashed */ public class Main extends MovieClip { //private var graph:ActiveGraph; //private var fps:FPSBox; private var game:Game; private var username:String = "ME"; private var opponent:String = "OCTO"; public var isMultiplayer:Boolean = false; public var isHost:Boolean = true; public function Main() { // Tests the game in singleplayer mode game = new Game(isMultiplayer); addChild(game); game.loadData(true, isMultiplayer, username, opponent); game.x = 10; game.y = 10; game.addEventListener(NPCEvent.SEND_GAME_OBJECT, dispatchEvent); //graph = new ActiveGraph(0,false,true,1); //graph.y = 360-15; //addChild(graph); //fps = new FPSBox(); //addChild(fps); //fps.x = 0; //fps.y = 360 - 30; } /* * When the engine needs to dispatch a game move */ private function onSendGameObject(evt:CheckersEvent):void { var move:Object = evt.data; trace("[Main] onGameObject " + move.fromx + "|" + move.fromy + "|" + move.tox + "|" + move.toy); dispatchEvent(evt); } /* * When the engine receives a game object from SmartFoxServer */ public function onGameObject(move:Object):void { trace("[Main] onGameObject " + move.fromx + "|" + move.fromy + "|" + move.tox + "|" + move.toy); game.onGameObject(move); } public function initialize(dir:String = ""):void { game.startGame(isHost); } public function loadData(obj:Object):void { trace("[Main] loadData " + obj.username + "|" + obj.opponent + "|" + obj.isMultiplayer + "|" + obj.isHost); username = obj.username || username; opponent = obj.opponent || opponent; isMultiplayer = obj.isMultiplayer; isHost = obj.isHost; game.loadData(isHost, isMultiplayer, username, opponent); } } }
Other Files
RefereeAbstract.as (Sample RefereeAbstract Interface) CheckersReferee.as (Sample CheckersReferee Class) ISymPlayer.as (Sample ISymPlayer Interface) SymPlayer.as (Sample SymPlayer Class) IGameView.as (Sample GameView Interface) GameView.as (Sample GameView Class)