一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Canvas2D mobile web game development – implementation | Tizen Developers

 gearss 2016-05-19

Introduction

Before reading this article we recommend you to get familiar with the “Canvas2D mobile web game development – basics” where we introduced Canvas 2D API along with concepts of the game loop and the adaptive game loop. We also shortly described a basic web game architecture. A class diagram for sample application - Earth Guard - was presented. That’s why we recommend you to start with the previous article.

In this article, we will concentrate on the implementation side. We will show you in detail how game primitives are built, how to put them all together and place on a game board. We will also explain how to create a flexible level representation. As a sample application we will use Earth Guard 1.0.3.

NOTE: The sample application, attached to this article, requires a device supporting the DEVICE ORIENTATION  event.

Game primitive types

In game module (./js/modules/game.js) there are different types of game primitives defined.

  1. var objectTypes = {
  2. PLAYER : 1,
  3. PLAYER_MISSILE : 2,
  4. ENEMY : 4,
  5. ENEMY_MISSILE : 8,
  6. STAR : 16,
  7. EARTH : 32,
  8. HUD : 64,
  9. POWER_UP : 128,
  10. MSG : 256,
  11. OVERHEAT : 512
  12. };

We did this to distinguish primitives that have graphical representation. Thanks to this approach, we can easily count the number of primitives of a given type that are currently on the board or define that primitive of a given type can collide only with primitive of other type. GameBoard class heavily relies on the above object types.

GameBoard class

GameBoard class (./js/classes/GameBoard.js) is a container class that aggregates many items in one game view. In some game frameworks this container is also called a scene. In case of our game architecture only one GameBoard object can be visible at a time. This approach simplifies the implementation. Below you can see that the GameBoard object can aggregate only Sprite objects instances as well as objects which classes inherit from Sprite class.

 

Fig 1: GameBoard class diagram

Responsibilities of the GameBoard class:

  1. Aggregation of primitives into one game view.
  2. Primitives number handling by a type. You can request the number of objects of each primitive type by calling (in this case we would like to know how many PLAYER objects are present on someBoard):
  1. someBoard.cnt[game.getObjectTypes().PLAYER]
  1. Primitive z-order index management, while adding objects to a board. The lower the zIndex you set, the lower the primitive will be displayed. When you set a 0 zIndex to some objects, they will be overlapped by primitives with higher zIndex. For better understanding of zIndex concept please refer to the below code and image.
  1. someBoard.add(somePrimitive, zIndex)

Fig 2: GameBoard zIndex concept. Missiles have the highest zIndex, HUD and Earth lower, while Enemies and Player Ship the lowest.

  1. Removal of all primitives from the board
  1. someBoard.removeAll()
  1. Primitives removal from the board by a given type
  1. someBoard.removeByTypes(game.getObjectTypes().PLAYER)
  1. Iterating through all the primitives in a given board. If you call the board’s iterate method the draw methods of all aggregated primitive’s will be invoked. It is worth mentioning that draw is a special method, because also the drawUnder method will be invoked before it and the drawOver method will be invoked after it. Why such thing happens will be described later on.
  1. someBoard.iterate(“draw”)
  1. Collision detection between primitives. In the example below we detect the first collision between given enemy object and any of the player missiles on someBoard. By first collision I mean that in one game loop iteration only one collision is detected, even if many missiles overlaps with the enemy primitive.
  1. someBoard.collide(enemy, game.getObjectTypes().PLAYER_MISSILE)

Fig 3: Only one GameBoard object can be visible at a time

Sprite class

Sprite class is defined in ./js/classes/Sprite.js and its main purpose is to use spriteSheet module (please refer the previous article: Canvas2D mobile web game development – basics for more information on sprites handling) in order to draw on Canvas the corresponding game primitive. Every game primitive, such as Enemy, PlayerShip and Explosion etc. inherits from the Sprite class. Below you can find a short description of Sprite class functionalities:

  1. Retrieves data from the spriteSheet module relevant to the given sprite, such as width, height, number of frames etc.
  2. Uses spriteSheet’s draw function to physically render primitives on canvas
  3. Defines stubs for drawUnder and drawOver methods that are usually overridden in classes that inherit from Sprite. The main purpose of those methods is to provide a possibility to render graphics over and under a given sprite in the same class. This is not a replacement for GameBoard’s z-index mechanism but rather an additional feature.

In our sample application there are no instances of Sprite class defined. We can say that Sprite is an abstract class in this project.

Fig 4: Sprite class diagram

Sample primitive design – PlayerMissile

Each primitive that inherits from a Sprite class needs to have a type defined. We must provide an implementation of a step method that is always invoked in the game loop before the draw method (for more information on the game loop, please refer to:  Canvas2D mobile web game development – basics). The step method is always used to calculate new primitive’s position. The only parameter that is passed to each Step method is called dt. This parameter is very important, because it is used in all motion equations through the game to speed up or to slow down the movement. Please refer to the below code of PlayerMissile class:

  1. "use strict";
  2. var PlayerMissile = function(x, y) {
  3. this.type = game.getObjectTypes().PLAYER_MISSILE;
  4. this.setup('missile', {
  5. damage : 10
  6. });
  7.  
  8. this.x = x - this.w / 2;
  9. // Use the passed in y as the bottom of the missile
  10. this.y = y - this.h;
  11. // y velocity of Player Missile
  12. this.vy = -700;
  13.  
  14. /**
  15. * Function: - change the y position of the missile, - checks if the PlayerMissile isn't outside the board (if yes - removes the missile), - checks if the PlayerMissile collides with Enemy,
  16. *
  17. * @param dt
  18. */
  19. this.step = function(dt) {
  20. this.y += this.vy * dt;
  21. if (this.y < config.player.topOffset) {
  22. this.board.remove(this);
  23. }
  24. var collision = this.board.collide(this, game.getObjectTypes().ENEMY);
  25. if (collision) {
  26. collision.hit(this.damage);
  27. this.hit();
  28. }
  29. };
  30. /**
  31. * Function defines how collision with other object affects the PlayerMissile (it destroys the missile and removes it from board)
  32. */
  33. this.hit = function() {
  34. this.board.add(new Explosion(this.x + this.w / 2, this.y + this.h, "explosion_yellow_small"), 100);
  35. this.board.remove(this);
  36. }
  37. };
  38. game.inherit(PlayerMissile, Sprite);

You can see that the first line in the step method is: this.y += this.vy * dt;

T in the following equations is the game loop period – this is explained in details in “Canvas2D mobile web game development – basics”.

This is a motion equation for Y axis, dt parameter is defined in game module (./js/game.js) as dt = T/1000. In our case T=2500, so dt = 2.5. We can write this motion equation as follows:

 V(n+1) = V(n) – 700 * 2.5 = V(n) – 1750

If we increase the game period (and decrease the FPS) the dt factor in each motion equation increases, because we need to move objects further in each game step. The dt parameter allows us to smoothly change the game FPS and not to affect the primitive’s speed observed by the user.

Enemy class

The enemy class provides implementation for enemy primitive in the game. It defines what are the possible collisions with the enemy object and the enemy’s motion equations with parameters.

Each enemy object is described using two motion equations:

Vx(t)= A + B * sin(C*t+D)

Vy (t)= E + F * sin(G*t+H)

Please refer to the two below images, where visualization of above equations is provided.

Fig 5: Vx(t) motion equation

Fig 6: Vy(t) motion equation

By default each parameter for motion equations is equal to 0. You can override as many parameters as you need in order to create complex motion equations. In the enemy class the step method is responsible for calculating a new position of the enemy – it has two motion equations defined.

  1. /**
  2. * Function changes the position and velocity of Enemy, fires missile
  3. * @param dt
  4. */
  5. this.step = function(dt) {
  6. this.t += dt;
  7. this.vx = this.A + this.B * Math.sin(this.C * this.t + this.D);
  8. this.vy = this.E + this.F * Math.sin(this.G * this.t + this.H);
  9.  
  10. this.x += this.vx * dt;
  11. this.y += this.vy * dt;
  12.  
  13. if (this.reload <= 0 && Math.random() < this.firePercentage) {
  14. this.reload = this.reloadTime;
  15. if (this.missiles == 2){
  16. this.board.add(new EnemyMissile(this.x, this.y + this.h), 100);
  17. this.board.add(new EnemyMissile(this.x + this.w, this.y + this.h), 100);
  18. } else {
  19. this.board.add(new EnemyMissile(this.x + this.w / 2, this.y + this.h), 100);
  20. }
  21. }
  22. this.reload -= dt;
  23. };

Enemy types

In Earth Guard there are many different types of Enemies defined – please refer to the ./js/config.js file. Each definition provides a basic enemy configuration that can be overridden in the level configuration that is explained below.

  1. var enemies = {
  2. /**
  3. * Attributes of enemy:
  4. * x - initial position (this will be multiplied by a random factor)
  5. * sprite - sprite of enemy
  6. * A - constant horizontal velocity
  7. * B - strength of horizontal sinusoidal velocity
  8. * C - period of horizontal sinusoidal velocity
  9. * D - time shift of horizontal sinusoidal velocity
  10. * E - constant vertical velocity
  11. * F - strength of vertical sinusoidal velocity
  12. * G - period of vertical sinusoidal velocity
  13. * H - time shift of vertical sinusoidal velocity
  14. * health - number of hits needed to kill the enemy
  15. * damage - damage done to the PlayerShip object
  16. * missiles - number of missiles to fire by enemy
  17. * firePercentage - percent of the missiles to be accually fired
  18. * pointsHit - points added for hitting the enemy by player
  19. * pointsDestroy - points added for destroying the enemy by player
  20. */
  21.  
  22. basic : {
  23. x : 100,
  24. sprite : 'enemy_purple',
  25. B : 100,
  26. C : 2,
  27. E : 100,
  28. health : 30,
  29. damage : 10,
  30. missiles : 2,
  31. firePercentage : 0.003,
  32. pointsHit : 15,
  33. pointsDestroy : 60
  34. },
  35.  
  36. straight : {
  37. x : 100,
  38. sprite : 'enemy_ship',
  39. E : 200,
  40. health : 30,
  41. damage : 10,
  42. firePercentage : 0.001,
  43. pointsHit : 10,
  44. pointsDestroy : 40
  45. },
  46.  

Sample enemy definition and visualization

Below we present part of the level definition for the 4th level in the Earth Guard game:

  1. types : [ {
  2. basicType : enemies.basic,
  3. overrides : {
  4. x : 350,
  5. B : 200,
  6. C : 1,
  7. E : 55,
  8. firePercentage : 0.008
  9. }
  10. } ],

 

The Y velocity: Vy(t) = 55. It means that the enemy will travel with a constant in time Y velocity equal to 55. The enemy movement for Vx(t)=0  is visible below.

Fig 7: Vy(t)=55 with Vx(t)=0

If we add X velocity equal to: Vx(t)=200*sin(t) the movement will change into sinusoidal, which is presented on the below image.

Fig 8: Vy(t)=55 with Vx(t)=200*sin(t)

Levels implementation

If you want your game to be more attractive for players you should think about various levels that differ in difficulty. From a developer point of view each level should be defined in a consistent way. You should also provide mechanisms for: starting, stopping and switching levels. In case of Earth Guard each level is limited by time. It means that the level will end regardless of the number of enemies displayed or killed. You can find the parameters that describe each level below (please refer: ./js/config.js):

  1. {
  2. period : 2300, //
  3. // Array with enemies that will be displayed very "period" time
  4. // In this case 2 enemies will be displayed each 2300msec.
  5. types : [ {
  6. basicType : enemies.straight, // basic enemy type
  7. overrides : { // all motion parameters can be overridden here
  8. x : 650,
  9. E : 45
  10. }
  11. }, {
  12. basicType : enemies.basic,
  13. overrides : {
  14. x : 400,
  15. B : 100,
  16. C : 1,
  17. E : 60
  18. }
  19. } ],
  20. duration : 90, // level duration in seconds
  21. name : "Tighter...", // level name
  22. powerUps : {
  23. number : 3 // number of power ups that will be displayed during the level
  24. }

Besides the level definition and implementation you also need a mechanism for levels management. For this purpose levelManager module was created - ./js/modules/levelManager.js. This provides methods to:

  1. Start a certain level
  2. Stop a level
  3. Get levels array
  4. Get the currently processed level
  5. Get already passed levels
  6. Store already passed levels

Please find the code below.

  1. "use strict";
  2. var levelManager = function() {
  3. var levels = [];
  4. var alreadyPassedLevels = [];
  5. for ( var i = 0; i < config.levels.length - 1; i += 2) {
  6. var nextLevel = new Level(config.levels[i + 1]);
  7. nextLevel.num = i + 2;
  8. var thisLevel = new Level(config.levels[i]);
  9. thisLevel.num = i + 1;
  10. thisLevel.options.nextLevel = nextLevel;
  11. if (levels[i - 1])
  12. levels[i - 1].options.nextLevel = thisLevel;
  13. levels.push(thisLevel);
  14. levels.push(nextLevel);
  15. }
  16.  
  17. return {
  18. init : function() {
  19. if (localStorage.getItem('levels')) {
  20. alreadyPassedLevels = $.parseJSON(localStorage.getItem('levels'));
  21. }
  22. },
  23.  
  24. /**
  25. * Starts given level
  26. *
  27. * @param {Number} number
  28. * @returns {Boolean} true in case of success
  29. */
  30.  
  31. start : function(number) {
  32. if (typeof levels[number - 1] === "undefined") {
  33. game.log("ERROR: Unable to find level " + number);
  34. return false;
  35. } else {
  36. var currLevel = this.getCurrentLevel();
  37. if (currLevel)
  38. currLevel.stop(true);
  39. levels[number - 1].start();
  40. return true;
  41. }
  42. },
  43.  
  44. /**
  45. * Returns all levels
  46. *
  47. * @returns
  48. */
  49.  
  50. getLevels : function() {
  51. return levels;
  52. },
  53.  
  54. /**
  55. * Returns current active level
  56. *
  57. * @returns
  58. */
  59.  
  60. getCurrentLevel : function() {
  61. for ( var i = 0; i < levels.length; i++) {
  62. if (levels[i].isRunning())
  63. return levels[i];
  64. }
  65. return false;
  66. },
  67.  
  68. /**
  69. * Method to force stop current level
  70. *
  71. * @returns
  72. */
  73.  
  74. stopCurrentLevel : function() {
  75. var cl = levelManager.getCurrentLevel();
  76. if (cl) {
  77. cl.stop(true);
  78. }
  79. },
  80.  
  81. /**
  82. * Stores already passed level in localStorage
  83. *
  84. * @param level
  85. * @returns
  86. */
  87.  
  88. storePassedLevel : function(level) {
  89. if (alreadyPassedLevels.indexOf(level.num) === -1) {
  90. alreadyPassedLevels.push(level.num);
  91. localStorage.setItem('levels', JSON.stringify(alreadyPassedLevels));
  92. game.log("adding level");
  93. } else {
  94. game.log("not adding level");
  95. }
  96. },
  97.  
  98. /**
  99. * Gets the levels that the player has already passed during previous games.
  100. *
  101. * @returns {Array} numbers of already passed levels
  102. */
  103.  
  104. getAlreadyPassedLevels : function() {
  105. var alreadyPassed = $.parseJSON(localStorage.getItem('levels'));
  106. if (!alreadyPassed)
  107. alreadyPassed = [];
  108. if (config.allLevelsVisible) {
  109. var alreadyPassed = [];
  110. for ( var i = 1; i <= this.getLevels().length; i++) {
  111. alreadyPassed.push(i);
  112. }
  113. }
  114. return alreadyPassed;
  115. }
  116. }
  117. }();

Summary

In this article, we described a sample implementation of a mobile web game on Tizen platform. Now you know what are the main responsibilities of the GameBoard class, how to define game primitives and how to handle levels. This architecture can be easily reused in other mobile web games developed in future that do not use any game frameworks.

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    欧美亚洲综合另类色妞| 嫩呦国产一区二区三区av| 欧美野外在线刺激在线观看| 婷婷开心五月亚洲综合| 麻豆视频传媒入口在线看| 天堂av一区一区一区| 国产成人精品一区二区在线看| 国产高清视频一区不卡| 天海翼精品久久中文字幕| 日本不卡视频在线观看| 隔壁的日本人妻中文字幕版| 日韩不卡一区二区视频| 国产成人精品一区二区三区| 亚洲国产精品国自产拍社区| 神马午夜福利一区二区| 91人人妻人人爽人人狠狠| 国产不卡最新在线视频| 微拍一区二区三区福利| 99久久精品免费精品国产| 97人妻精品一区二区三区男同| 国产情侣激情在线对白| 国产乱人伦精品一区二区三区四区| 中文字幕亚洲在线一区| 国产色偷丝袜麻豆亚洲| 午夜午夜精品一区二区| 免费在线观看欧美喷水黄片| 欧美成人高清在线播放| 视频一区中文字幕日韩| 香港国产三级久久精品三级| 中文字幕一区二区三区大片| 日韩欧美综合中文字幕| 不卡一区二区高清视频| 中文字字幕在线中文乱码二区| 又黄又色又爽又免费的视频| 精品一区二区三区乱码中文| 日韩一区二区三区在线欧洲| 黄色片国产一区二区三区| 午夜亚洲精品理论片在线观看| 国产亚洲中文日韩欧美综合网| 亚洲综合激情另类专区老铁性| 在线免费国产一区二区|