Jump to content

JavaScript "Game State" handling


astralaaron

Recommended Posts

I am just wondering if anyone here has any advice on this. I know this isn't a game development forum but I am sure you all know javascript and html5 is becomming popular. I have been programming games with c++ and java, I use a Singleton method for managing game states. I am not sure how to go about it in javascript, from reading around I am hearing that since javascript uses prototyping it is not a "true" OOP language and singleton methods won't work. So what would a good way be to handle a game state? I guess it would be possible to pass them to a new page on the website for each state but that does not seem like a very good option. thanks for any advice

Link to comment
Share on other sites

What state are we talking about? The state within a page? The state across pages (including page refreshes) on the same device and browser? Or the state across devices, browsers, refreshes per user?The state within a page can be kept within a single JavaScript object, which is the closest you can get to a singleton. In JavaScript, an object is a little like the Map template of C++ or JAVA - you have key/value pairs, but the difference is that different values can be of different types, and one of these types can be a function... which is how you get stuff like "document.getElementById()". What is often called "the getElementById() function" for short is really the "getElementById" key in the "document" variable, which is an object, containing that key among others. And the value of this key is a function.The state across pages and page refreshes can be kept within cookies or DOM storage.To keep the state across devices, browsers and pages per user, you'll need to use a server side language in addition to JavaScript, and store the state in a database.

Link to comment
Share on other sites

Javascript has a particular syntax for singletons:

var GameState = {    "property1" : 0x80,    "property2" : "value 2",    "property3" : true,    "method1" : function(arg) {	    this.property1 = 0x40;	    this.property2 = arg;    },    "method2" : function() {	    var value;	    if(property3) {		    return (property1 & 0x80);	    } else {		    return (property1 & 0x7F);	    }    }}

Link to comment
Share on other sites

What state are we talking about?
I am just talking about being able to go from a "Title menu" to "Level 1" and "Level 2" in a game. No refreshing, and the graphics would be drawn on to the html5 canvas. What Ignolme posted looks like what I need, but how do you implement your states from that? Is this going to work like an interface in Java or will this work different? Edited by astralaaron
Link to comment
Share on other sites

AFAIK, Java has a canvas UI element.So... yeah, it would pretty much work out the same as if you were drawing everything on the canvas in Java. The only difference is that repaints happen as soon as you call a canvas function, which you can call at any event handler, unlike in Java, where (if I remember correctly) repaints happen at a particular event (that you could trigger from any other event), which is only the only event from which you can perform draws.

Link to comment
Share on other sites

For the drawing part, I create a new canvas element (document.createElement("canvas")), I draw on it and when everything is drawn I use the drawImage() method to draw the contents of that canvas onto the visible canvas.You need to use clearRect() to delete everything on the backbuffer canvas before beginning a new frame in the game cycle.

  • Like 1
Link to comment
Share on other sites

( I almost did not post this because it is a very big post...it is the full C++ game state manager with one game state called TestState, I am sorry for such a long post. If anyone is interested in getting this code working in c++ pm me and I can help with that..) Ok here is the C++ singleton game state manager, I can also show a java game state manager that works the same way if it would help figure out a way with javascript.(this code example is using SDL as the graphics library if anyone wants to use it)

#ifndef _GAME_STATE_H_#define _GAME_STATE_H_ #include "Game.h" class GameState { 	public: 		virtual void init() = 0;		virtual void clean() = 0; 		virtual void handleInput(Game* game) = 0;		virtual void update(Game* game) = 0;		virtual void draw(Game* game) = 0; 		virtual void pause() = 0;		virtual void resume() = 0; 		void changeState(Game* game, GameState* state) {			game->changeState(state);		} 	protected: 		GameState(){} };#endif

Then each state of the game inherits the GameState class and must implement all of the functions declared as virtual in the GameState class

#ifndef _TEST_STATE_H_#define _TEST_STATE_H_ #include "GameState.h" class TestState : public GameState { 	public: 		void init();		void clean(); 		void handleInput(Game* game);		void update(Game* game);		void draw(Game* game); 		void pause();		void resume(); 		static TestState* instance() {			return &g_TestState;		} 	protected: 		TestState(){} 	private:		static TestState g_TestState;}; #endif

here is the implementation if the TestState which just draws a rectangle on the screen.

#include "TestState.h" TestState TestState::g_TestState; void TestState::init(){}void TestState::clean(){} void TestState::handleInput(Game* game){	SDL_Event event;	if(SDL_PollEvent(&event)) {		switch(event.type){			case SDL_QUIT:				GameInst::Instance()->quit();				break; 				default:break; 		}	}} void TestState::update(Game* game){} void TestState::draw(Game* game){	SDL_Rect offset; 	offset.x = 10;	offset.y = 10;	offset.w = 100;	offset.h = 100; 	SDL_FillRect(GameInst::Instance()->getScreen(), &offset, SDL_MapRGB(GameInst::Instance()->getScreen()->format, 0x00, 0x55, 0x00));} void TestState::pause(){}void TestState::resume(){}

#ifndef SINGLETON_H#define SINGLETON_H template <class T>class Singleton{public:	static T* Instance()	{		static T myT;		return &myT;	}};#endif

The game class is the class that makes the game window and holds a vector (array) of the GameState objects. The last element in that array is the current state.The Game class also has changeState, pushState, and popState functions to change between states.

#ifndef _GAME_H_#define _GAME_H_ #include <SDL/SDL.h>#include "Singleton.h"#include <vector> using namespace std; class GameState; class Game { 	public: 		Game(); 		void init(const char* title, int screen_width, int screen_height, int screen_bpp, bool fullscreen);		void clean(); 		void handleInput();		void update();		void draw(); 		void changeState(GameState* state);		void pushState(GameState* state);		void popState(GameState* state); 		bool isRunning(){ return b_gameRunning; }		void quit() { b_gameRunning = false; } 		SDL_Surface* getScreen() { return display; } 	private: 		vector<GameState*> states;		SDL_Surface* display; 		friend class Singleton<Game>; 		bool b_gameRunning;		bool b_fullScreen;}; typedef Singleton<Game> GameInst; #endif

here is the implementation of the game class take a look at how the changeState method works, this is what I really want to emulate in Javascript.

#include "Game.h"#include "GameState.h" Game::Game(){} void Game::init(const char* title, int screen_width, int screen_height, int screen_bpp, bool fullscreen){	int flags = 0;	SDL_Init(SDL_INIT_EVERYTHING); 	if(fullscreen) flags = SDL_FULLSCREEN; 	display = SDL_SetVideoMode(screen_width, screen_height, screen_bpp, flags); 	SDL_WM_SetCaption(title,title); 	b_fullScreen = fullscreen;	b_gameRunning = true;} void Game::clean(){   SDL_FreeSurface(display);   SDL_Quit();} void Game::handleInput(){	states.back()->handleInput(this);}void Game::update(){	states.back()->update(this);}void Game::draw(){	states.back()->draw(this);	SDL_Flip(display);} void Game::changeState(GameState* state){	if(!states.empty()){		states.back()->clean();		states.pop_back();	}	states.push_back(state);	states.back()->init();}void Game::pushState(GameState* state){	if(!states.empty()) {		states.back()->pause();	}	states.push_back(state);	states.back()->init();} void Game::popState(GameState* state){	if(!states.empty()) {		states.back()->clean();		states.pop_back();	}	if(!states.empty()) {		states.back()->resume();	}}

then finally the main.cpp file will run the init in the Game class then add the TestState to the array of states with changeState

#include "Game.h"#include "TestState.h" int main(int argc, char* argv[]) { 	GameInst::Instance()->init("test",400,600,32,false); 	GameInst::Instance()->changeState(TestState::instance()); 	Uint32 start;	while(GameInst::Instance()->isRunning()) {		start = SDL_GetTicks(); 		GameInst::Instance()->handleInput();		GameInst::Instance()->update();		GameInst::Instance()->draw(); 		if((1000/60) > SDL_GetTicks() - start) {			SDL_Delay((1000/60) - (SDL_GetTicks() - start));		}	} 	GameInst::Instance()->clean(); return 0;}

At this point you can just copy the TestState.h and TestState.cpp for each new level in your game which makes it easy to organize a game with many levels. (Sorry for such a long post) I hope that somebody can look at this and see how something similar can be done in JavaScript. If anyone is having trouble in C++ with what I have posted let me know and I will help. on http://27software.net/canvas/ I have a basic game loop working with javascript with a rectangle you can move around the screen, so I am good on how to draw on the canvas but thanks for that advice.

Link to comment
Share on other sites

I'm not sure if "state" is the right word to describe that, a state is typically just information about the current environment, I wouldn't expect to see a draw function as part of a state class. Anyway, there are methods in Javascript to simulate things like inheritance even though it doesn't formally have it. You can create an object and extend that object to create an inherited object. That's where the prototypes come in. The stack would just be an array that you can push on and pop from, which is supported natively. http://www.google.com/search?client=opera&rls=en&q=javascript+inheritance&sourceid=opera&ie=utf-8&oe=utf-8&channel=suggest

Link to comment
Share on other sites

I'm not sure if "state" is the right word to describe that, a state is typically just information about the current environment, I wouldn't expect to see a draw function as part of a state class.
Maybe GameLevel would be a better name. Each 'state' class is basically a mini game. Each one has an array of objects, updating/rules, input handling, and drawing. I am reading through the Javascript Inheritance google search and there is a lot of good information there that is helping me understand the syntax. The more that I read and mess with this I am thinking maybe I don't even need to inherit from a parent class for this. I am starting to think this because in C++ I am pretty sure that the reason is so that the vector (array) can have a universal 'type' for all of the game states. In Javascript I think It wouldn't be a problem that they were not of the same parent type (I may be wrong, I am about to go mess with it now.) As long as the classes that I make for states have the needed methods it may just work. I will post it if I figure anything out. Edited by astralaaron
Link to comment
Share on other sites

This seems like it will work out. Like I was saying I don't think you need to inherit the class at all in JS, the functions in the parent class are all empty in C++. After doing this test I think I won't have a problem organizing the levels. Here it is for anyone that is interested (it is not a full game level manager just a test)

<!DOCTYPE html><html> <head> <script type="text/javascript">var states = new Array();   function AState(){  this.something = "a";  }  AState.prototype.init = function() {  this.something = "A SOMETHING";  }  AState.prototype.getSomething = function() { return this.something; }   function BState(){  this.something = "b";  }  BState.prototype.init = function() {  this.something = "B SOMETHING";  }  BState.prototype.getSomething = function() { return this.something; }var a = new AState();var b = new BState(); document.addEventListener("DOMContentLoaded", function() {  states.push(a);  alert('still working');  alert(states[states.length - 1].getSomething());  states[states.length - 1].init();  alert(states[states.length - 1].getSomething());   states.pop();  states.push(;   alert(states[states.length - 1].getSomething());  states[states.length - 1].init();  alert(states[states.length - 1].getSomething());    var sa = document.getElementById('asd');  var sb = document.getElementById('xyz');  sa.addEventListener("click", function(){   alert('level complete, entering new level');   states.pop();   states.push(a);   alert(states[states.length - 1].getSomething());  }, false);   sb.addEventListener("click", function(){   alert('level complete, entering new level');   states.pop();   states.push(;   alert(states[states.length - 1].getSomething());  }, false); }, false);</script></head><body> <p id="asd">level A</p><p id="xyz">level B</p></body></html>

Thanks for the assistance

/*EDIT:  I have a typo in that code where I used states.push(; instead of states.push(;for some reason I am not able to edit it on this post.  It is a bit confusing to me though because the code still works with that typo.isn't Javascript case sensitive? EDIT 2: well..for some reason I cannot enter a lowercase b into the forum I guess I did have it correct in my code so that would explain it not failing. the forum is capitalizing my b's*/ (  // there...if you put the parentheses around a b the forum capitalizes it.

Edited by astralaaron
Link to comment
Share on other sites

For the drawing part, I create a new canvas element (document.createElement("canvas")), I draw on it and when everything is drawn I use the drawImage() method to draw the contents of that canvas onto the visible canvas.You need to use clearRect() to delete everything on the backbuffer canvas before beginning a new frame in the game cycle.
After reading your post again I see exactly what you are saying, does this make the animations any smoother in JS? I haven't noticed a flicker. Even if it doesn't I think I will be doing it as you are saying because it makes good sense.
Link to comment
Share on other sites

When you're doing a heavy amount of drawing you will start to notice that things are being drawn and erased on the screen. If the application is very simple that won't happen.

  • Like 1
Link to comment
Share on other sites

For the drawing part, I create a new canvas element (document.createElement("canvas")), I draw on it and when everything is drawn I use the drawImage() method to draw the contents of that canvas onto the visible canvas.You need to use clearRect() to delete everything on the backbuffer canvas before beginning a new frame in the game cycle.
I am trying to do this right now and having some trouble getting the back buffer to copy over to my viewable canvas.
var buffer = document.createElement("canvas");buffer.width = SCREEN_W;  //also tried buffer.w and buffer.hbuffer.height = SCREEN_H;var backBuffer = bufferHtml.getContext("2d");alert(backBuffer); //this worksalert(backBuffer.width); // undefined var htmlCanvas = document.getElementById("game");var gameScreen = htmlCanvas.getContext("2d"); backBuffer.fillStyle = "#ff0000";backBuffer.fillRect(0,0,10,10); gameScreen.drawImage(backBuffer, 0, 0);

Please let me know what I am doing wrong, I am messing around with it and searching around but can't seem to find a fix. EDIT: yes SCREEN_W and SCREEN_H are defined, 640 / 480

Edited by astralaaron
Link to comment
Share on other sites

The height and width are properties of the <canvas> element, not the drawing context, so you aren't going to see a width property on the context. Here's how you do it:

// Get a reference to a <canvas> element in the documentvar screen = document.getElementById("my_canvas");// The width and height are obtained from the attributes of the <canvas> element// (<canvas width="..." height="...">)var SCREEN_W = screen.width;var SCREEN_H = screen.height; // Create a backbuffer with the same width and height as the front endvar backBuffer = document.createElement("canvas");backBuffer.width = SCREEN_W;backBuffer.height = SCREEN_H; // Reference to contextsscreenContext = screen.getContext("2d");bufferContect = buffer.getContext("2d"); // Clear the back buffer before drawing:function clearFrame() {    bufferContext.clearRect(0, 0, SCREEN_W, SCREEN_H);} // Draw the contents of the backbuffer to the screenfunction renderFrame() {    screenContext.clearRect(0, 0, SCREEN_W, SCREEN_H);    screenContext.drawImage(backBuffer, 0, 0)} /* The action happens here */clearFrame();bufferContext.fillStyle = "#ff0000";bufferContext.fillRect(0,0,10,10);// The drawn image won't appear until renderFrame() is called:renderFrame();

  • Like 1
Link to comment
Share on other sites

Ignolme your example is very clear however the image is still not drawing. I have stepped through each line with alerts to find where it is failing and it seems like on this line it is failing:

gameScreen = drawImage(backBuffer, 0, 0);

an alert immediately before that line with shows the backBuffer variable is ok:

alert(backBuffer); // outputs: [object CanvasRenderingContext2D]

This is strange..

Link to comment
Share on other sites

drawImage() is method of the canvas context so it need to call like context.drawImage()

Link to comment
Share on other sites

have you check the type of gameScreen to be sure?

Link to comment
Share on other sites

can you post your html part, css part of canvas styling and all the js code if its not the whole?

Edited by birbal
Link to comment
Share on other sites

Ok here is a test I put together, and it is failing exactly the same way as my project.

<!DOCTYPE html><html><head>  <title>test</title>  <style type="text/css">   body {	margin:0;   }    #wrapper {	margin:0px;	position:relative;	width:100%;   }   #container {	margin:0 auto;	width:640px;	height:480px;   }  </style>   <script type="text/javascript">     function test() {	var that = this;	alert('constructing'); 	var SCREEN_W;	var SCREEN_H;	var screenContext;	var bufferContext;	this.init = function() {	 alert('initiating');	 var screen;	 screen = document.getElementById("mycanvas");	 SCREEN_W = screen.width;	 SCREEN_H = screen.height;		 var backBuffer = document.createElement("canvas");	 backBuffer.width = SCREEN_W;	 backBuffer.height = SCREEN_H;		 screenContext = screen.getContext("2d");	 bufferContext = backBuffer.getContext("2d");		 that.clearFrame();	 bufferContext.fillStyle = "#ff0000";	 bufferContext.fillRect(0,0,10,10);	 that.renderFrame();	 alert('after test');	}  	this.clearFrame = function () {	 bufferContext.clearRect(0, 0, SCREEN_W, SCREEN_H);	 alert('frame clear');	}	this.renderFrame = function() {	 alert('rendering');	 screenContext.clearRect(0, 0, SCREEN_W, SCREEN_H);	 alert('test');	 screenContext.drawImage(bufferContext, 0, 0); // this seems to be failing, same line in my project	 alert('rendered'); //this alert never executes	}   }     var t = new test();   document.addEventListener("DOMContentLoaded", t.init ,false);  </script> </head><body>  <div id="wrapper">    <div id="container">  	<h1>Test</h1>  	<canvas id="mycanvas" width="640" height="480">	 upgrade your browser	</canvas>     </div>  </div></body></html>

Edited by astralaaron
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...