Comprehensive analysis on how to use AngularJS to develop 2048 games (1)
One of the frequently asked questions is when Angular framework is a bad choice. My default answer is that when you write a game, Angular has its own event loop processing ($ digest loop), and the game usually requires a lot of underlying DOM operations. if Angular supports many types of games, this statement is not accurate. Even if the game requires a lot of DOM operations, this may use the angular framework to process static parts, such as recording the highest score and game menus.
If you are as fascinated with the popular 2048 games as you are, the goal of the game is to use the same value to sum up the square with a value of 2048.
In this blog today, we will use AngularJS to create a copy from start to end and explain the entire process of creating an app. Because this app is relatively complex, I plan to use this article to describe how to create a complex AngularJS application.
This is the demo we want to create.
Start now!
TL; DR:The source code of this app can also be downloaded. The link of the app on github is located at the end of the article.
Step 1: plan the app
The first step is to design the app to be created at a high level. Whether it's a fake app or a self-built app, this step has nothing to do with the size of the app.
Let's take a look at this game. We found a pile of tiles at the top of the game board. Each tile can be used as a position to place other numbered tiles. Based on this fact, we can hand over the task of moving tiles to CSS3 for processing, instead of relying on JavaScript. It needs to know the position of moving tiles. When there is a tile on the game panel, we just need to make sure it is placed at the right position on the top.
The layout with CSS3 can bring us CSS animation effects, and AngularJS behavior is also used by default to track the game board status, tiles, and game logic.
Because we only have one single page, we also need a single controller to manage the page.
Because the application has only one game board in its lifecycle, a single instance in the GridService contains all the grid logic. Because the service is a singleton mode object, this is a proper location for a storage grid. We use GridService to handle tile replacement, movement, and grid management.
Put the logic and processing of the game into a service called GameManager. It will be responsible for the status of the game, processing the movement, and maintaining the score (current score and highest score)
Finally, we need a component that allows us to manage the keyboard. We need a service called KeyboardService. In this blog post, we have implemented the application's desktop processing. We can also use this service to manage touch operations and make it run on mobile devices.
Create an app
To create an app, we first create a basic app (using the yeoman angular generator to generate the app structure, this step is not necessary. we only use it as the starting point, and then quickly separate it from its structure .). Create an app directory to store the entire application. Use the test/directory as the same directory of the app/directory.
The following instructions are for setting up the project using the yeoman tool. If you prefer to do it manually, you can skip installing the dependencies and move on to the next section.
Because we use the yeomanin tool in the application, we must first ensure that it has been installed. yeoman is installed based on NodeJS and npm. the installation of NodeJS is not described in this tutorial, but you can refer to the NodeJS.org site.
After npm is installed, we can install the yeoman tool yo and angular generators (called by yo to create an Angular app ):
- $ npm install -g yo
- $ npm install -g generator-angular
After installation, we can use the yeoman tool to generate our application as follows:
- $ cd ~/Development && mkdir 2048
- $ yo angular twentyfourtyeight
This tool will ask for some requests. We can select "yes". We do not need any dependency except angular-cookies.
Note that using the Angular generator, it will keep CT you have the compass gem installed along with a ruby environment. See the complete source for a way to get away without using ruby and compass below.
Our angular Module
We will create a scripts/app. js file to store our applications. Create an application now:
- angular.module('twentyfourtyeightApp', [])
Module structure
The layout structure used by angular applications is currently recommended based on functions, rather than types. That is to say, we can define the module structure based on functions without dividing components into controllers, services, and commands. For example, define a Game module and a Keyboard module in an application.
The module structure clearly separates the functional domains that match the file structure. This not only facilitates the creation of large and flexible angular applications, but also facilitates sharing of functions in the app.
Finally, we set up a test environment to adapt to the file directory structure.
View
The most easy-to-cut part of an application is not a view. Looking at the view itself, we found that there is only one view/template. In this application, there is no need for multiple views, so we create a single <div> element to place the content of the application.
In our main file app/index.html, we need to include all the dependencies (including angular. js itself and JS files, namely scripts/app. js), as follows:
- <!-- index.html -->
- <doctype html>
-
-
- <title>2048</title>
- <link rel="stylesheet" href="styles/main.css">
-
- <body ng-app="twentyfourtyeightApp"
- <!-- header -->
- <div class="container" ng-include="'views/main.html'"></div>
- <!-- script tags -->
- <script src="bower_components/angular/angular.js"></script>
- <script src="scripts/app.js"></script>
- </body>
-
Feel free to make a more complex version of the game with multiple views-please leave a comment below if you do. We 'd love to see what you create.
With the app/index.html file set, we need to process the view in app/views/main.html in detail at the application view level. When you need to import a new
To modify the index.html file.
Open app/views/main.html and we will replace all the views specified by the game. Using the controllerAs syntax, we can clearly know in $ scope where we are going to query data and which Controller is responsible for which component.
- <!-- app/views/main.html -->
- <div id="content" ng-controller='GameController as ctrl'>
- <!-- Now the variable: ctrl refers to the GameController -->
- </div>
ThecontrollerAssyntax is a relativelyNewSyntax that comes with version 1.2. It is useful when dealing with uncontrolled controllers on the page as it allows us to be specific about the controllers where we recommend CT functions and data to be defined.
In the view, we want to display the following items:
The static game header can be completed as follows:
- <!-- heading inside app/views/main.html -->
- <div id="content" ng-controller='GameController as ctrl'>
- <div id="heading" class="row">
-
- <div class="scores-container">
- <div class="score-container">{{ ctrl.game.currentScore }}</div>
- <div class="best-container">{{ ctrl.game.highScore }}</div>
- </div>
- </div>
- <!-- ... -->
- </div>
Note that when currentScore and highScroe are referenced in the view, we also reference the GameController. controllerAs syntax so that we can explicitly reference the controllers we are interested in.
GameController
Now we have a reasonable project structure. Now we can create a GameController to place the values we want to display in the view. In app/script/app. js, we can create a controller in the main module twentyfourtyeight. App:
- angular
- .module('twentyfourtyeightApp', [])
- .controller('GameController', function() {
- });
In the view, we reference a game object which will be set in GameController. This game object will reference the main game object. We create this main game module in a new module to place all the references in the game.
Because this module has not been created, the app will not load it in the browser. In the controller, we can add the GameManager dependency
- .controller('GameController', function(GameManager) {
- this.game = GameManager;
- });
Don't forget, we are creating a module-level dependency, which is a different part of the application, so make sure it is loaded correctly in the application, we need to list it as a dependency of the angular module. To make the Game module a dependency on the twentyfourtyeightApp, We will list it in the array that defines this module.
Our entire app/script/app. js file should look like this:
- angular
- .module('twentyfourtyeightApp', ['Game'])
- .controller('GameController', function(GameManager) {
- this.game = GameManager;
- })
Game
Now that we have views connected to each other, we can start to write the logic behind the game. To create a new game module, we create our module as app/scripts/game. js in the app/scripts/directory:
- angular.module('Game', []);
When building modules, we like to write them in their own directory named after the module. we'll implement the module initialization in a file by the name of the module. for instance, we're buildingGameModule, so we'll build our game module inside theapp/scripts/gamedirectory in a file namedgame. js. This methodology has provided to be scalable and logical in production.
The Game module provides a single core component: GameManager.
In the future, GameManager will be completed so that it can process the game status. Users can move different methods, record scores, determine when the game ends, whether the user breaks the highest score, and whether the user loses.
When developing an application, we like to compile the stub method for the method we use, write the test code, and fill in the place to implement it.
For the purposes of this article, we'll run through this process for this module. When we write the next several modules, we'll only mention the core components we shoshould be testing.
We know that GameManager will support the following features:
With these features, we can create the basic outline of the GameManager service and write the test code for it:
- angular.module('Game', [])
- .service('GameManager', function() {
- // Create a new game
- this.newGame = function() {};
- // Handle the move action
- this.move = function() {};
- // Update the score
- this.updateScore = function(newScore) {};
- // Are there moves left?
- this.movesAvailable = function() {};
- });
After the basic functions are implemented, You can compile the test code to define the functions required by GameManager.