Quick and easy API, browser support is what makes Canvas appealing. But, as is often the case, simplicity is also a weakness. Without difficulty, for example, you can draw a rectangle, a circle, a line or hang an image. But developing useful content on this simple basis is a bit more difficult.

On the example of game development, an approach to animation and control of a game object is shown.
The motive to play around with the development of the game on Canvas were two of these examples:
Examples are illustrative, but, after a couple of minutes chasing the balls and tearing half a dozen nets, the excitement was gone. There is no plot, no goal, no sense - in the end, cool, but not interesting.
')
Prologue
Working with Canvas was described in the publication of the
HTML page on Canvas . In general, the exact same approach, including architecture and development tools, was applied here.
Compatibility was tested on FF (Windows, Linux Mint), CR and IE (Windows). Check on the available gadgets, too, was, but without a special result (about this at the end).
GitHub code:
ArcadSpotsTensTetrGitHub demo (with offline mode):
ArcadSpotsTensTetrGame is model management
The game consists of: game model, model management functional and model display functionality. The ultimate essence of the game comes down to changing the model.
The model consists of game objects. Objects from basic primitives: rectangles, circles, lines.
The model and the functionality that provides the dynamics of the model, do not know anything about how the model will be shown. The Canvas functionality only knows how to display the static state of the model at the time of rendering. In the process of drawing, model changes are not allowed. Fortunately, this is not necessary to follow (provided by single-loop event loop-a).
The Canvas API is native, and therefore fast, and can display a large number of base primitives in real time. Therefore, the number (tens and even hundreds) of simple objects is not so critical for Canvas. But complex computational objects implemented in JavaScript, this is something that should be avoided. The same can be attributed to the implementation of algorithms for changing the model. The simpler and easier they are (the more native, in this case, the Canvas API will be taken into account), the better. If, according to the game, it is necessary to implement complex and / or time-consuming computational algorithms in it, then, for such purposes, you can use web workers (not discussed here).
The design of the model must be considered carefully from the very beginning. If the development process has to significantly change the model, this will entail reworking the control and display code.
Model development
Tetris made a good demo to test the approach (I'm not the first here).
Model concept:
- Blocks consisting of colored cells fall into the well and go into the common pool. The filled pool row is deleted. The game ends when the well is full and there is no free space for the new block.
- Blocks can be rotated and moved horizontally. Rotation and movement is possible only in the free space of the well. When the block is rotated and the well pool is filled, the color of the cells remains.
- The base object is a cell. The cell has a pixel size. This size is the main base, on the basis of which the entire rendering on the Canvas takes place.
- A well is a virtual grid consisting of cells. Block and pool control - in changing the number of the column and row of cells in the well.
Description of the well and cell size:
APELSERG.CONFIG.SET.CellSize = 20;
Base object (cell):
APELSERG.MODEL.Cell = function (cellRow, cellCol, cellColor) { this.Row = cellRow;
Object (block):
APELSERG.MODEL.Block = function (blockType) { this.type = blockType;
The redundancy of the block model is visible, but due to this, two tasks are solved: 1. a block with all states is formed at a time (it is easy to continue to control the block); 2. cell colors are preserved when the block is rotated.
Blocks are created in game mode:
APELSERG.MODEL.GetNewBlock = function() { var newBlock = APELSERG.CONFIG.PROC.NextBlock;
New unit:
APELSERG.MODEL.GetBlock = function() { var blockType = APELSERG.MODEL.GetBlockType();
Here you can add blocks of any configuration. The main thing is to assign a new type to the block and add this type to the array in the function APELSERG.MODEL.GetBlockType ().
Model management
Management takes place from the keyboard in a typical way:
window.addEventListener('keydown', function (event) { ... }
Blocks fall on the timer (but not on window.requestAnimationFrame). The APELSERG.CONFIG.SET.LevelTick array stores the time period value for the current level:
APELSERG.MAIN.Animation = function (makeStep) { if (makeStep) { APELSERG.MODEL.BlockShift('DOWN');
To check whether a block can move or rotate in a given direction, a virtual block is created and conditions are checked. If the verification conditions are met, the real block is moved to a new location. Check function:
APELSERG.MODEL.CheckBlockCross = function(block) { var canShift = true;
Block offset occurs by simply assigning cell numbers:
APELSERG.MODEL.ShiftBlockColumn = function(block, num) { for (var x = 0 in block.cells) { for (var n = 0 in block.cells[x]) { block.cells[x][n].Col = block.cells[x][n].Col + num; } } }
When the bottom is reached, the cells from the block are moved to the pool of the well with the function APELSERG.MODEL.DropBlockToPool (). At the same time points are awarded.
The functionality that is implemented, but, in my opinion, was not very successful (it is not in the settings):
- APELSERG.CONFIG.PROC.FastDownFlag = false. If set to true, the drop will not be instant, but visualized.
- APELSERG.CONFIG.SET.ShowFullRow = false. If set to true, the filled row will be shown before deletion.
- APELSERG.CONFIG.SET.SlideToFloor = false. If set to true, the block after the reset will "slide" on the floor and go to the pool only by the tick of the timer.
Rendering on Canvas
The APELSERG.CANVA.WellRewrite () function is responsible for drawing the model on the Canvas. It is very simple and well documented. All it does is clear the Canvas and consistently draw the model primitives.
User interface
Settings, Results, Help
After the model came to life, it became clear that for the holistic game of one revived model is not enough. This is how the UI modules appeared:
- APELSERG.UI. To provide interfaces to settings, results and help.
- APELSERG.LANG. To ensure easy localization.
This is a typical dynamic DOM, perhaps not the most successful (the code is simple, I will not describe).
Local storage
To maintain interest in the game, you need to store settings, results, and perhaps offline mode is useful. For storage, localStorage is used. Technologically, everything is implemented in a typical way, but it is useful to trace the connection with global objects APELSERG.CONFIG.SET and APELSERG.CONFIG.RESULT.
A few notes:
- Each domain uses its own localStorage.
- It is necessary to use localStorage.clear () with great care - it will clear all localStorage for the current domain.
The configuration is stored in two objects:
- CONFIG.SET - static settings that are applied when the application starts (saved in localStorage).
- CONFIG.PROC - dynamic settings that are used during the operation of the application (not saved in localStorage).
The storage name must be unique and is formed from a combination of several static variables:
APELSERG.CONFIG.SET.Version = "0-1-0" APELSERG.CONFIG.SET.LocalStorageName = "APELSERG-ArcadPlain"; APELSERG.CONFIG.GetLocalStorageConfigName = function () { return APELSERG.CONFIG.SET.LocalStorageName + "-Config-" + APELSERG.CONFIG.SET.Version; }
The configuration is saved when it changes. This is done simply (even for a separate function is not drawn). In the function APELSERG.UI.ApplySettings () (module UI), added two lines:
var configName = APELSERG.CONFIG.GetLocalStorageConfigName(); localStorage[configName] = JSON.stringify(APELSERG.CONFIG.SET);
When the application starts, the presence of the saved configuration in localStorage is checked and, if the configuration has been saved, it is restored:
APELSERG.CONFIG.GetConfigOnLoad = function () { if (APELSERG.CONFIG.PROC.LoadFromWeb) { var configName = APELSERG.CONFIG.GetLocalStorageConfigName(); if (localStorage[configName] !== undefined) { APELSERG.CONFIG.SET = JSON.parse(localStorage[configName]); } } }
LocalStorage may be empty or not used at all. Empty localStorage happens: 1. at the first start; 2. if not saved; 3. if has been cleared. Configuration cleaning may be needed mainly during the development process. For example, the configuration has changed - variables have been added or removed, and the application continues to work, as if it does not see any changes, as the old configuration object is restored from the storage.
If the application was launched from a local disk, the local storage is disabled. This is done because browsers do not support this mode very well. But the rest of the functionality of the application is preserved. Starting from a local disk is controlled at startup:
window.location.protocol == "file:" ? APELSERG.CONFIG.PROC.LoadFromWeb = false : APELSERG.CONFIG.PROC.LoadFromWeb = true;
Results are stored in APELSERG.CONFIG.RESULT. Functionally, the storage of results is identical to the storage configuration.
Autonomous work
The offline mode (Application Cache or AppCache) allows you to continue working with the web application when the Internet is turned off. In general terms, setting up different offline conditions can be quite complicated. But, in our case, this is one of the simplest procedures.
You need to prepare a manifest file for offline mode (game_arcad_plain.appcache.txt):
CACHE MANIFEST
# Ver 0.1.0
# 001
game_tetr_plain.htm
game_tetr_plain_canva.js
game_tetr_plain_config.js
game_tetr_plain_lang.js
game_tetr_plain_main.js
game_tetr_plain_model.js
game_tetr_plain_model_blocks.js
game_tetr_plain_ui.js
It is necessary to add a link to this file in the HTML element of the web page:
<html manifest="game_arcad_plain.appcache.txt">
Thin moment with the extension "txt". The “appcache” or “manifest” extension is recommended with the text / cache-manifest MIME type. The demo is done this way because it was curiously
lazy .
"# 001" is needed to reload files on the client at the initiative of the server. If files are updated on the server, they will not get to the client until the manifest file changes. And what can be changed in it? - a comment on "# 002".
Other games
After the first game was developed, the rest of the games were stamped on the model. 80 percent of the code remained identical, and the changes concerned, basically, only models (the model and control code became simpler). Therefore, to describe these games separately does not make sense, except for a few nuances:
- Since there is a constantly moving object (ball) in these games, the animation is performed on window.requestAnimationFrame.
- The ball moves slower / faster not by changing the time interval, but by changing the coordinate increments in X and Y. The time intervals are not measured (and, in a good way, it would be necessary).
- Rebound is considered from the center of the ball, taking into account the diameter. Acceleration and direction are randomly selected. When moving back, the ball does not see the racket. The thickness of the block / racket can not be thinner than the size of the ball, otherwise, at high speeds, there can be through-gaps.
Brief test findings
What well:
- Animation on Canvas is simple. The model makes working with Canvas even easier.
- Local storage and battery life are easy to use.
What is not very good:
- Canvas is not on friendly terms with touchscreens. Even if the application reacts to the touchscreen, anyway, the behavior of a mouse click on a 20-inch display and a finger pointing at the screen of a smartphone will differ. Therefore, demo games are intended mainly for use on desktop systems, and management is focused on the keyboard. The positive thing is that having a relatively simple, working desktop application, you can proceed to its further adaptation.
- Canvas, periodically, lags a little. There are no mechanisms for fighting.
useful links
HTML5 CanvasHTML5 Local StorageHTML5 Application Cache