This is a very simple game involving dodging planets and collecting glowing stars. There is a two-layer parallax repeating starfield in the background. This uses sprites and the RenderKit to create the hardware accelerated rendering of the game-play. This is all wrapped up into a component. This is really designed to be a singleton and made fullscreen, there are global variables in the code which mean putting multiple instances on the same page will result in them sharing a lot of the same behavior.
Here is the file with the JavaScript and the sprite and atlas you need to add to your Umajin project vertical_dodge.zip. If you would like to change the images in the sprite-sheet, they are in the space_textures.zip file. This file includes the individual images, the atlas file and a project file for CodeAndWeb’s TexturePacker, a very handy tool for making sprite-sheets.
We are not going to include all of the code in this article, grab all the code from the zip file at the top of the example.
We are going to describe the two main function that you might want to modify. The first is the init function. This is what creates all of the subcomponents for the game. The first is the renderkit. This is the primary component and holds all of our background sprites, planet sprite and the ship sprite. Then we create a text component to hold the score.
Next, we actually create all the sprites inside the renderkit and finally bind events to the down, move and up events – AND importantly the tick event. You need to be careful using the tick event as it fires every frame. This is great for animation, but you need to make sure you don’t do things which will be slow or your component will make your application laggy or unresponsive.
registerComponent("vertical_dodge", "", "vertical_dodge", "Vertical Dodge", "vertical_dodge_init", "vertical_dodge_resize", "vertical_dodge_refresh", "", "on_gameover") function vertical_dodge_init(width, height){ //tell the component to clip anything hanging out the edge of the component setProperty(self,"clip_to_bounds",1) // create a gamekit rk = createComponent(self, "renderkit", 0, 0, 100, 100) setProperty(rk,"spritesheet","space"); g_text = createComponent(self, "text",0,0,100,10); setProperty(g_text,"resize_type", 2); setProperty(g_text,"font_size", 28); setProperty(g_text,"font_align", 1); setProperty(g_text,"center_vertical", 1); setProperty(g_text,"font_color", '0xFFFFFFFF'); setProperty(g_text,"text", "0"); g_width = getProperty(self,'width'); g_height = getProperty(self,'height'); //scale so sprites are same size whatever DPI g_scale = g_width / 1000.0; //background setup_sprite_tile('bkg1'); setup_sprite_tile('bkg2'); //ship renderkitSpriteCreate(rk, -1, 'ship', 0, 0 ,g_scale ,0 , 255); //falling things for(var i=0; i<g_fallingThings.length; i++){ g_fallingThings[i].xp = Math.random(); g_fallingThings[i].x = g_fallingThings[i].xp * g_width; g_fallingThings[i].y = (400 - (i * 350)) * g_scale; var cx = g_fallingThings[i].x; var cy = g_fallingThings[i].y renderkitSpriteCreate(rk, -1, g_fallingThings[i].sprite, cx, cy ,g_scale ,0 , 255); } // register for the per frame tick event bindEvent(rk,"on_tick","vertical_dodge_on_tick"); bindEvent(rk,"on_down","vertical_dodge_on_down"); bindEvent(rk,"on_move","vertical_dodge_on_move"); bindEvent(rk,"on_up", "vertical_dodge_on_up"); }
Here is the drawing event called every tick. This is where the main work is done. First the position is used to determine an offset for the stars in the background (the parallax effect). Then the main ship position is updated and defaults are setup for the first frame.
Then the background layers are moved, the ship is moved and the falling things are all updated.
While looping through the falling things we check to see if they have hit the top or tail of our spaceship and crash raising the game over event. If the item we hit is a star however – we gets points instead. Finally we check if any falling items are off the bottom and we restart them at the top with a new speed and position.
function vertical_dodge_draw(){ g_width = getProperty(rk,'width'); g_height = getProperty(rk,'height'); g_scale = g_width / 1200.0; //update positions var offset_x = -g_x; var offset_y = g_tick * 5; g_sx = (g_x + g_sx) / 2.0; g_sy = (g_y + g_sy) / 2.0; //set a default if starting if (g_x==0){ g_score = 0; var p = getComponentParent(rk) g_text = getComponentChild(p,1); setProperty(g_text,"text", g_score); g_mode = 1; g_sx = g_width / 2.0; g_sy = g_height / 1.2; g_x = g_sx; g_y = g_sy; for(var i=0; i<g_fallingThings.length; i++){ resetFallingThing(i); } } //move background update_sprite_tile(0,'bkg2', offset_x * 0.2, offset_y * 0.2) update_sprite_tile(1,'bkg1', offset_x * 0.3, offset_y * 0.3) //move ship renderkitSpriteUpdate(rk, 32, -1, 'ship', g_sx, g_sy - g_scale*150, g_scale, 0, 255) //move falling things for(var i=0; i<g_fallingThings.length; i++){ g_fallingThings[i].y += g_fallingThings[i].vy; if (g_fallingThings[i].bonus == 1){ // flashing bonus renderkitSpriteUpdate(rk, 33 + i, -1, g_fallingThings[i].sprite, g_fallingThings[i].x, g_fallingThings[i].y ,g_fallingThings[i].scale ,0 , 160 + 90 * Math.sin(g_tick / 10.0)); }else{ //normal solid falling thing renderkitSpriteUpdate(rk, 33 + i, -1, g_fallingThings[i].sprite, g_fallingThings[i].x, g_fallingThings[i].y ,g_fallingThings[i].scale ,0 , 255); } //check if we have hit a falling thing if (g_fallingThings[i].hit == 1){ var noseconeDistance = lineDistance(g_sx, g_sy - 300*g_scale, g_fallingThings[i].x, g_fallingThings[i].y) var engineDistance = lineDistance(g_sx, g_sy, g_fallingThings[i].x, g_fallingThings[i].y) if (engineDistance < g_fallingThings[i].scale * 190 || noseconeDistance < g_fallingThings[i].scale * 190){ if (g_fallingThings[i].bonus == 1){ g_score += 1; var p = getComponentParent(rk) g_text = getComponentChild(p,1); setProperty(g_text,"text",g_score); resetFallingThing(i); }else{ var p = getComponentParent(rk) g_text = getComponentChild(p,1); setProperty(g_text,"text","Game Over : " + g_score); g_mode = 0; //raise event print('raise event') raiseEvent(p, 'on_gameover','{"value":"1"}'); } } } //check if falling thing is off the bottom if(g_fallingThings[i].y > g_height * 1.33){ resetFallingThing(i); } } }