Developing a game on AS3 (Part 1: introduction, utilities)

Purpose: development of a computer game
Target audience: beginners, interested, onlookers
Tools: ActionScript 3.0 **


Remark 0:
I will try to achieve relative autonomy for each part, which will allow reading them optionally and out of order.


Note 1:
The text of the article may contain the personal opinion of the author on certain issues. All responsibility for inciting heated senseless disputes rests with the reader ( 18+ ).
Cycle content





Introduction



Note 2:
In the process of reading, you will often see the word “tool” in relation to certain things. This is done on purpose. I believe that a developer to solve certain problems is able to choose the most suitable tool for this and use it wisely.


Note 3:
In the process of reading, you will also often see the word “process”. It will be used both in the broad sense of the word and in the narrowed technical sense. Without a process, there is no creativity. Without a process, there is no development.

You may have noticed that I am writing “computer game development” without explicitly indicating which one. It is not just that. On the one hand, we can say: “it doesn’t matter what you write, the main thing is how” *. On the other hand, as you probably noticed again, the article has several parts, because this, among other things, is a whole story. So let the final product be some intrigue of the entire series of articles. In fact, the fact that I present the material through history emphasizes that without a process there is no development.
For a computer game developer** - This is not just some program, but also some process. During this process (creative, or creative in places, or not creative in places), a lot of events occur that affect the final product, other products and the process itself, the developer and the people around him.

If you do not take into account the “squeeze the maximum profit” model, then for a start some initial impulse is important - an idea that will captivate you to one degree or another. You draw, enliven the process (in imagination, on paper, in a graphical editor) ... etc.
The whole story began when, once again, in the meantime, I looked at the logo of one famous i-company ...

I took up my idea (it fascinated me so much that I didn’t check it for “everything has already been stolen to us” *) and began to draw something on paper. Created text files with brief notes on game mechanics. These were not only technical aspects, but simply texts, names of musical compositions and games. He grabbed the first medium that came to hand ( Flash ?!) And began to create graphics for the game. Flash** allowed me surprisingly easy to translate my drawings from imagination into specific graphic elements, causing a desire to work on. I knew that later I could use them directly in my game, which added a few points to my enthusiasm. I read a lot of articles (including here on habrahabr.ru **) on game development. About the fact that you need to give up the idea of ​​creating a game alone. I watched a video of someone notch creating a game in real time, which increased my unwillingness to agree with “N reasons to quit everything, take three hundred drops of ethereal valerian and forget about sleep” *.

Having accumulated a sufficient amount (the sufficiency criterion is not defined) of graphic materials, having in my imagination everything is already working as it should, I began to code. “At first, the Soviet invasion was successful” *, but soon the process stalled. “And then he still stalled. And still stalled. And yet ... ”*

But I was lucky, the starting impulse was so strong that it did not allow me to completely abandon the project. Hurrah!..

So, in the development process, I wanted the player to entertain himself with another game during a pause. Something very simple and warm. I needed a game that could fulfill this role. The process was in full swing, and the option came pretty quickly. I switched to a new game, with the same enthusiasm. The process was repeated in miniature, but because the game was simple, its creation did not stall. On the contrary, the completion of a small game gave an additional impetus to the development of the main one. In the next part, we will develop it together. In the meantime ...
During work, a lot of things changed in the program code, because it always did not suit the developer (and does not suit). I wanted my changes not to spoil an already working game. Then I turned to TDD **. I had to understand what it was:Kent Beck. Test Driven Development: By Example **.


Note 3:
TDD is “not an Avada Kedavra ** to solve all development problems” *. Everyone needs to use it wisely. In particular, it is a convenient tool for making changes and fixing errors. Although I used this technique for development, I can’t say that I have always adhered to the red / green / refactoring algorithm indicated in it.

So I'm with ActionScript ** and I need a simple testing tool. Sounds like a task.

There are probably some ready-made tools ( review , for extras ) ** for these purposes. However, wanting to get a little deeper in the spirit of testing, I decided to invent a bicycle.


Note 4:
Dear reader, in my opinion, the invention of a bicycle is not bad. By doing something your own, you get invaluable experience and a basic understanding of how this something works. You encounter the problems that those who have already solved this problem faced. It's like learning the basics of programming (you need to learn, try to implement sorting, although in most cases you will just call the “sort ()” method or the like). However, it is unlikely that your bike, written in a couple of hours, will be able to compete with existing solutions (congratulations, if you can). Therefore, in the future it is worth paying attention to a specific tool from "people who have caught more than one bug on this" *.


Utilities


We will document the code in the javadoc ** style .


Note 5:
I believe that code can and should be written so that comments are not needed. However, if we write the library “for export”, the question arises of providing the consumer with decent documentation. For such purposes, javadoc ** comments are a good tool to easily create such documentation.


Note 6:
ActionScript ** supports the absence of type checking, but it also provides the ability to monitor them. I believe that if a language provides a tool for tracking types at the syntax level, they should be used. You need to use type checking everywhere, and use the absence of checking wisely where a certain functionality is achieved due to this. Thanks to type tracking, the compiler will detect many errors at the compilation stage, saving your time in the long run for something more interesting than debugging.


Testing tool


Git: github.com/v-alx-v/as3-unit-tests **
Task: you need the most simple tool that could be used without further pain in the subsequent development of games. Design as a pluggable library with a minimum entry threshold.

Inspired by JUnit **, considering the limitations of ActionScript **, wanting to create the simplest possible tool, I decided to make, perhaps not a very convenient, but very transparent set of tools (without explicit involvement of Hogwarts specialists **).

Total: class STester , abstract class SUnitTest small manager SUnitTestsfor organizing a transparent test run environment, test run configuration script, “launch file”.
In Figure 2, you can examine a simple UML ** diagram of the classes developed.


Figure 2
Unit Test.  UML  Class chart developed

In order to test a new class, you need to create a new class for it, using СUnitTest as the base class, add a new class to the launch configuration and run the “launch file”.
class CTester
It can be used separately during debugging as a tool for checking expected values ​​(or you can not use it).
There is nothing complicated here, however, we will not completely relieve CTester of dependency ( СUnitTests ), because it is more interesting in the aggregate, and not separately:


    /**
     * Invokes error in test process.
     * @param strTitle String
     * @param strComment String
     */
    public function error( strTitle:String, strComment:String = ''):void
    {
      this.m_bError = true;
      CUnitTests.error( strTitle, strComment);
    }


CUnitTest class
“There is a desire to declare an abstract class, but in ActionScript ** there is no such possibility. It is possible to declare an interface, but we have some common implementation, so there is no such desire. So let's drink for that ... "*.

We will consider the ordinary class as abstract, and thanks to the system of exceptions, we will not allow our abstraction to be used with impunity:


    /**
     * Gets list of functions to run.
     * @return Array of Function
     */
    protected function testList():Array
    {
      throw new Error( 'testList must be implemented');
    }

The method described above assumes that a descendant class defines it. The essence of the method is to return a list of class functions that need to be run (in JUnit ** you would mark such functions with @ Test annotation ).
class CUnitTests
This is a test runtime environment (a set of static data and functions). Below you will find out that you actually do not need to work with this class, although it explicitly appears in the test run configuration script.

Of the interesting features - we can ask the environment to use our CMyTester extends CTester for testing. It will be provided to all interested in this object:


    /**
     * Tester fabric.
     * @param strLabel String
     * @return {@link alx.common.test.CTester}
     */
    public static function createTester( strLabel:String):CTester
    {
      return new CUnitTests.s_testerClass( strLabel);
    }

So, when using the environment, the CUnitTest class is of actual interest (by expanding it you can create all user tests) and the startup configuration script:


  import alx.common.test.*;
  CUnitTests.init( CTester, CUnitTests.SIMPLE_MODE, true);
  CUnitTests.run();
  CUnitTests.printResult( root);

In order to add a new test, we need to import its class and add it to the list to run.
So for a test, for example, test.alx.common.test.CExampleUnitTest extends CUnitTest, you need to change the configuration as follows:


  import alx.common.test.*;
  import test.alx.common.test.CExampleUtitTest;
  CUnitTests.init( CTester, CUnitTests.SIMPLE_MODE, true);
  CUnitTests.run( CExampleUtitTest);
  CUnitTests.printResult( root);

After starting the “startup file”, the user will receive a window filled with red or green color depending on the overall test result (see Figure 1 ). In the console, you can familiarize yourself with the specific testing progress: with the version of the framework and the settings with which it is run, as well as what specific tests (classes) ( EXTENDED_MODE ) and what specific functions in them ( FULL_MODE ) were run with the test results for each aspect separately. If a failure occurs, then debugging information and a call stack will be displayed (if you asked for it with a special setting).
By the way ...
  1. Design template applied ** Factory method ** public static function createTester (strLabel: String): CTester implemented through a kind of Prototype ** CUnitTests.s_testerClass ;




Random number generator


Git: github.com/v-alx-v/as3-random **
Task: a random number generator is needed that supports repetition of a pseudorandom sequence.

ActionScript ** is so strange that it does not include a base class for generating random numbers with support for the possibility of repeating a pseudorandom sequence.
Quickly looking at the solution to the problem on the Internet, I did not find a generator that completely suits me. Either during the generation, some kind of “ksoroshvigovaya” magic is created, then the problem is transferred to the noise generator in the bitmap, or something else.
Having opened the source code for Random for Java **, I decided to port the solution to ActionScript**. Unfortunately, nothing came out in fifteen minutes. As a result, another strange implementation was implemented. But, having decidedly decided, I nevertheless remade the solution into a solution closer to Java **.
The random number generator was also considered by me from the standpoint of testing. How to test programs that use random numbers?

It seems that the answer is “Luke, you now have the opportunity to repeat the pseudo-random sequence” *. It looks convincing. However, taking into account my experience in developing a random number generator, I realized that in the future, you might want to change the implementation of the generator (make the distribution of pseudorandom numbers closer to the continuous distribution ** or provide the ability to specify the desireddistribution ** ***). The new solution will also be able to repeat pseudo-random sequences, but not the same as before. This means that you need:

  1. Implement the new CNewRandom extends CRandom class . Then the "old" code will use the "old" version of the generator without a simple opportunity to fix things (you can argue that if you write the program correctly, then, in fact, just change the configuration. Yes, but not all of them are correct). In addition, CRandom remains as garbage that can not be taken out of the garbage (we call it the effect of the grandmother);
  2. Update existing class. Then the "old" code is updated with an elementary replacement. There is no garbage. However, tests that rely on certain random sequences stop working;
  3. Think and redo everything.

Of course, option 3
Results of internal voting:
  1. 0% (0);
  2. 0% (0);
  3. 100% (1).


After some wanderings, I still came to an acceptable option. To begin with, an interface for a random number generator was created, and its general functionality is localized in an abstract class. Now you can create the generator itself:
  • It is recommended that you operate a random number generator everywhere only through its interface;
  • You can create an “improved random number generator” CNewRandom extends CRandom so that the “old” code, if necessary, can replace the “old” generator with a new one and get rid of garbage;

In addition to the usual random number generator, I created a "random number generator for testing" CFakeRandom extends CRandom :
  • It is guaranteed that with CFakeRandom all pseudo-random sequences are constant;
  • For added convenience, CFakeRandom has an additional opportunity to “ask to generate a certain sequence of pseudorandom numbers”:


        /**
         * Asks generator to generate certain sequence.
         * @return {@link CFakeRandom this}
         * @throws ArgumentError if arguments are not valid
         */
        public function ask( ...arValue):CFakeRandom
        {
          for ( var i:int = 0; i < arValue.length; i++)
          {
            var nValue:Number = arValue[ i];
            if (( nValue < 0) || ( nValue >= 1))
              throw ArgumentError( "Values should be in [0;1)");
          }
          this.m_arRandomSequence = arValue;
          return this;
        }
    

    After all, sometimes this is what you want.


    Note 7:
    Perhaps the reader will write that this function can be transferred to IRandom , recommend its use in tests, and remove CFakeRandom . Yes, but I believe that this function can be used exclusively for testing. Using such a function in the application code is wrong and harmful. Therefore, IRandom simply does not have to provide the user with a designated feature to prevent its misuse.
  • It is recommended to use it in all tests. Then you can change the Random without any particular concern (an attentive reader will still note that in this case, changing the Random can negatively affect the "old" code).

So, there is the Сandom class (somewhat reminiscent of Java Random **), which must be used through the IRandom interface . When testing, instead of Сandom , use СFakeRandom . There is nothing more to describe here, except to bring a simple UML diagram of the developed classes in Figure 3 .


figure 3
Random  UML  Class chart developed
By the way ...
  1. Apply a design pattern ** Bridge ** IRandom - CAbsctractRandom .




Collections


Git: github.com/v-alx-v/as3-collections **
Task: convenient collection of objects is needed.

And again on the Internet you can find many ready-made solutions: github.com/danschultz/as3-collections **, www.as3commons.org/as3-commons-collections **.

I did not use them, but at the same time it seems to me that they would completely suit me. We will be stubborn to the end and realize something of our own. Let it possibly be crooked, but we cannot miss the opportunity to practice.

In general, there is nothing to describe here, because it is just a reproduction of the finished solution. We can only note the fact that during the implementation of ActionScript** Again turned out to be rather strange. For a long time I tried to find an adequate way to calculate the hash code for an arbitrary object, but after spending more than an hour on this problem, I spat on everything and implemented HashMap at least somehow. Well, all the strength is that you can later return to this problem if necessary and change the implementation without fear of breaking the existing code (for example, if the creators of ActionScript ** change their minds).

Naturally, Java ** collections were taken as the basis . Here I would like to make a digression ...

The fact is that this is not the first time I have undertaken to implement collections in ActionScript**, but this is the time when I came to at least some result, and did not abandon everything at the beginning of the journey. In the process of implementation, I still do not understand some of the things that I encountered while studying the openjdk ** source code .

Problem:
Suppose there is a Collection ** interface and a List extends Collection ** interface .
Question: authors do not believe in inheritance and define methods in List that are already defined in Collection ?

Problem:
Suppose there is an abstract class AbstractCollection ** and there is an ArrayList extends AbstractList extends AbstractCollection **.
Question:I also understand the redefinition of some methods to delegate a task to another object, which is probably due to the improved performance of these methods. But why redefine the isEmpty method, I refuse to understand. Replacing the generic public boolean isEmpty () {return size () == 0;} with a private public boolean isEmpty () {return size == 0;} is unfortunate. Based on the rest of the code, I don’t believe that it’s just to “save” on the method call.

However, all this is just some little things that may indicate a lack of foresight of the author of this article.


Conclusion


“I will not bore you, esteemed reader, with further details ...” *.

In the next issue:
  • We get acquainted with recursion by the example of developing a mini-game for the main game for playing during a pause while playing the main game.



Note 8:
The text has been verified by the author, MS Word ** and a person who knows the spelling. For errors, please contact the author directly with a written complaint.


Note 9:
This is my first post on habrahabr.ru **. Be extremely cruel.



What is hidden behind the stars?
* - Used (directly or paraphrased) some well-known quote in their circles.
** - Some (we will not name names) could pay extra for advertising.
*** - It seems to me that you can create an even more elegant solution to the described problem by reviewing the very approach of generating random numbers towards the laws of distribution of a random variable. But this is not necessary for my case either, and I don’t want to bore the reader with additional text. If this topic is interesting, you can consider this issue separately.

P.S
  1. Why doesn't the acronym tag accept the title attribute ?
  2. How to insert text like [dog] [text] into the article and not get a link to the user?
  3. The author considers his code with the desire to open it to the public with the only restriction: preservation and attribution. One of the tasks now is to study the available open licenses for which one to choose or based on which to write your own. Consultations are welcome. Although I explicitly indicate here that I am going to present the code to the public, I want to note that until the relevant projects have received the appropriate license file, it is better not to use them.


Also popular now: