While I have my January game ready, I am at my parent's place while the game is in a different city so I will not be posting it until January 2nd when I return to University.
JavaScript did not have "proper" classes until ECMAScript 6. The problem is that while there is now a class keyword that makes creating classes much easier in browsers that have an ECMAScript 6 complaint version of JavaScript, this is not all browsers. Eventually there will be so few browsers that do not support ECMAScript 6 that using JavaScript classes will make sense, but before that transition takes place, the old way of creating JavaScript classes must be used. The nice thing is that backwards compatibility means that the old way will still work on new browsers.
The ECMAScript 6 way of creating classes is with a class construct. Within this construct we have a constructor which initializes the class and then methods that the class knows. There is some special support for getter and setter methods which I will not be covering here as this book is more focused on older JavaScript but that may be something I cover in a future book. Here is what a soldier class would look like in ECMAScript 6 versions of JavaScript:
// General function for printing to the web page so results can be seen
// without requiring the console
function printTestLine(s) {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now append the new line to this element
results.innerHTML = results.innerHTML + "
" + s
}
class E6Soldier {
constructor(name, rank, serialNumber) {
this.name = name;
this.rank = rank;
this.serialNumber = serialNumber;
}
printNRS() {
printTestLine("Name: " + this.name + " Rank: " + this.rank +
" Serial Number:" + this.serialNumber);
}
}
printTestLine("ECMAScript 6 tests");
var soldier1 = new E6Soldier("Bob", "Private", 12345);
var soldier2 = new E6Soldier("Doug", "Captain", 23456);
soldier1.printNRS();
soldier2.printNRS();
You will notice that both soldiers simply used the E6Soldier class to build their object. The creation of an object from a class is called instantiation in object oriented terminology. Every object of a particular class is known as an instant of that class. Instances share the same properties and methods, though object specific properties and methods can be manually added to specific objects if desired, but are otherwise independent.
The old way of creating classes is similar to the new way but instead of grouping things into a class structure you are creating a function. This function sets up the properties just like the constructor above did and then sets up all the methods that are used by the class.
function Soldier(name, rank, serialNumber) {
this.name = name;
this.rank = rank;
this.serialNumber = serialNumber;
this.printNRS = function() {
printTestLine("Name: " + this.name + " Rank: " + this.rank +
" Serial Number:" + this.serialNumber);
}
}
Note that it is not necessary to have all the methods defined within the function. Some people actually like defining the methods of a class outside of the class. This is done by using the function's prototype, which is something we will be talking about in more detail in later sections.
Soldier.prototype.changeRank = function(newRank) {
this.rank = newRank;
}
Using the classes is pretty much the same the old way as it is the new way.
var soldier3 = new Soldier("Pete", "Private", 34567);
var soldier4 = new Soldier("Billy", "Captain", 45678);
soldier3.printNRS();
soldier4.printNRS();
soldier4.changeRank("General");
soldier4.printNRS();
The real power of classes comes from polymorphism and inheritance, but that is also where the biggest problems of object oriented programming come from. Next section we will start our exploration of polymorphism and inheritance.
Saturday, December 29, 2018
Saturday, December 15, 2018
5.2 Creating Objects
There are a number of ways to create an object. The standard way of creating an instance of a class is to use the new constructor, which we will be covering in the next section. There is a special class that all classes are based off of called Object. This class can be used to create a new basic object which properties and methods can be attached to. To create an object using the new keyword is done as follows:
var myObj = new Object();
Properties can be added to an object by simply giving the property a value. This adds an interesting quirk to JavaScript programs as null indicates that an existing property has no current value attached to it while undefined means that the indicated property has not been attached to an object. For people use to typed languages this can be confusing as undefined and null are different things. You can check to see if a property is defined by using the following:
if (myObj.variable === undefined)
// handle undefined variable here
Object Oriented programmers generally refer to functions that are part of an object as methods. As was discussed in Chapter 3, functions can be assigned to a variable. This means that to add a method to an object you simply need to add it as you would any other property. You can create a new function specifically for the object or you can use an already existing function and simply attach it to the object.
// here is a comomon function that will be shared by multiple objects
function printNRS() {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now we are adding the info onto the text contained within the element
results.innerHTML += "
Name: " + this.name + " Rank: " + this.rank +
" Serial Number:" + this.serialNumber;
}
// Build first soldier by using an object and adding properties to it
var soldier1 = new Object();
soldier1.name = "Bob"
soldier1.rank = "Private";
soldier1.serialNumber = 12345;
// Methods can be added too
soldier1.printNRS = printNRS;
soldier1.makeCaptain = function() { this.rank = "Captain"};
Notice that in order to reference a property of the object that is calling the function, the this keyword is used. Scope works a bit different than in most other languages and is function based. This is why the function printNRS can access the properties of the object as it is called from that object so holds the object's scope. We will come back to this topic in a bit more detail when we cover closures in a later section.
As mentioned earlier, there is more than one way of creating an object. JavaScript has a shorthand way of creating properties and pairing them to values so that it is easier to create objects with multiple properties initialized. This feature of JavaScript was expanded upon to come up with the JSON data transfer format which is why many people refer to creating objects using key : value pairs as being in JSON format. This shorthand method simply lets you define the contents of the object by using {} to indicate the object and then placing the property name followed by a colon followed by the value. It is possible to have functions within this declaration. Once the object is created you can add additional properties or method as you did with the soldier1 object. So, lets create a second soldier:
var soldier2 = { name : "Doug", rank : "Captain",
serialNumber : 23456,
makeMajor : function() {
this.rank="Major"
}
};
// adding addtional methods, such as this common method, is possible
soldier2.printNRS = printNRS;
JSON is an acronym and stands for JavaScript Object Notation. It was originally created as a data transfer standard between JavaScript and whatever language is being ran on the server-side. The idea is that objects get converted into strings to be transferred then converted into objects at the destination. It is much simpler and more condensed than XML so has largely replaced XML usage. If you are receiving JSON strings from a server or other input file you can easily convert the strings into JavaScript by using the built-in JSON class as is done to create our third soldier.
var soldier3 = JSON.parse('{ "name" : "Billy", "rank" : "General", "serialNumber" : 42}');
soldier3.printNRS = printNRS;
Manually creating multiple versions of what is essentially the same object does seem like a lot of work. There is a better way which we will cover in the next section.
var myObj = new Object();
Properties can be added to an object by simply giving the property a value. This adds an interesting quirk to JavaScript programs as null indicates that an existing property has no current value attached to it while undefined means that the indicated property has not been attached to an object. For people use to typed languages this can be confusing as undefined and null are different things. You can check to see if a property is defined by using the following:
if (myObj.variable === undefined)
// handle undefined variable here
Object Oriented programmers generally refer to functions that are part of an object as methods. As was discussed in Chapter 3, functions can be assigned to a variable. This means that to add a method to an object you simply need to add it as you would any other property. You can create a new function specifically for the object or you can use an already existing function and simply attach it to the object.
// here is a comomon function that will be shared by multiple objects
function printNRS() {
// we are grabbing an html element
var results = document.getElementById("testResults");
// now we are adding the info onto the text contained within the element
results.innerHTML += "
Name: " + this.name + " Rank: " + this.rank +
" Serial Number:" + this.serialNumber;
}
// Build first soldier by using an object and adding properties to it
var soldier1 = new Object();
soldier1.name = "Bob"
soldier1.rank = "Private";
soldier1.serialNumber = 12345;
// Methods can be added too
soldier1.printNRS = printNRS;
soldier1.makeCaptain = function() { this.rank = "Captain"};
Notice that in order to reference a property of the object that is calling the function, the this keyword is used. Scope works a bit different than in most other languages and is function based. This is why the function printNRS can access the properties of the object as it is called from that object so holds the object's scope. We will come back to this topic in a bit more detail when we cover closures in a later section.
As mentioned earlier, there is more than one way of creating an object. JavaScript has a shorthand way of creating properties and pairing them to values so that it is easier to create objects with multiple properties initialized. This feature of JavaScript was expanded upon to come up with the JSON data transfer format which is why many people refer to creating objects using key : value pairs as being in JSON format. This shorthand method simply lets you define the contents of the object by using {} to indicate the object and then placing the property name followed by a colon followed by the value. It is possible to have functions within this declaration. Once the object is created you can add additional properties or method as you did with the soldier1 object. So, lets create a second soldier:
var soldier2 = { name : "Doug", rank : "Captain",
serialNumber : 23456,
makeMajor : function() {
this.rank="Major"
}
};
// adding addtional methods, such as this common method, is possible
soldier2.printNRS = printNRS;
JSON is an acronym and stands for JavaScript Object Notation. It was originally created as a data transfer standard between JavaScript and whatever language is being ran on the server-side. The idea is that objects get converted into strings to be transferred then converted into objects at the destination. It is much simpler and more condensed than XML so has largely replaced XML usage. If you are receiving JSON strings from a server or other input file you can easily convert the strings into JavaScript by using the built-in JSON class as is done to create our third soldier.
var soldier3 = JSON.parse('{ "name" : "Billy", "rank" : "General", "serialNumber" : 42}');
soldier3.printNRS = printNRS;
Manually creating multiple versions of what is essentially the same object does seem like a lot of work. There is a better way which we will cover in the next section.
Saturday, December 1, 2018
C5.1 Namespaces
When you are writing small programs you may never run into the namespace problem. Once you start using libraries, especially if you use multiple libraries, the namespace problem comes into play. Let us say you had a library that had a function for displaying hello. This library is called namespaceIssue.js and may look something like this:
message = "Hello!"
function sayHello() {
return message
}
Now lets say you wrote the following page that uses the library:
<!DOCTYPE html>
Namespace issue demo
Namespace issue demo
You would expect this program to display the words hello and goodbye. This is not what happens. Instead, it displays goodbye twice. Why? The library used the variable message to store the message that the sayHello function would display. The script in the html file uses message to store the goodbye message to display. It overwrites the variable which the library was using. For a small program like this it is a trivial problem to solve, but when the size of your program increases, especially if you are using code provided by third parties, this problem of variable (and function name) collision becomes a huge problem.
The solution to this problem is namespaces. The idea here is that you have a unique namespace for your program and all the variables for that program go into that namespace. Since each namespace is separate, you can have the same variable name in each of the namespaces without any conflict. In JavaScript, namespaces are created by creating a object with the name of the namespace. Here is the demo above re-written to use namespaces.
namespceUsed = {}
namespceUsed.message = "Hello World!"
function sayHello() {
return namespceUsed.message
}
It is possible to nest namespaces by having another namespace object as part of a namespace object. This is very commonly done to have library namespaces stored under a single unique namespace that you have control of. The convention is to use your domain as the unique name with appropriate names for the libraries under that domain. Some companies will take this a step further by having the root domain as the first namespace object, followed by their domain, followed by their library. This leads to the problem of declaring a namespace without overwriting other namespaces. The solution is to use the following way of declaring a namespace:
com = com || {}com.spelchan = com.spelchan || {}
It is a good habit to use namespaces for any script that you are loading. I generally don’t bother with the com root namespace but if Spelchan was a more common word, then it may be a good idea.
message = "Hello!"
function sayHello() {
return message
}
Now lets say you wrote the following page that uses the library:
<!DOCTYPE html>
Namespace issue demo
You would expect this program to display the words hello and goodbye. This is not what happens. Instead, it displays goodbye twice. Why? The library used the variable message to store the message that the sayHello function would display. The script in the html file uses message to store the goodbye message to display. It overwrites the variable which the library was using. For a small program like this it is a trivial problem to solve, but when the size of your program increases, especially if you are using code provided by third parties, this problem of variable (and function name) collision becomes a huge problem.
The solution to this problem is namespaces. The idea here is that you have a unique namespace for your program and all the variables for that program go into that namespace. Since each namespace is separate, you can have the same variable name in each of the namespaces without any conflict. In JavaScript, namespaces are created by creating a object with the name of the namespace. Here is the demo above re-written to use namespaces.
namespceUsed = {}
namespceUsed.message = "Hello World!"
function sayHello() {
return namespceUsed.message
}
It is possible to nest namespaces by having another namespace object as part of a namespace object. This is very commonly done to have library namespaces stored under a single unique namespace that you have control of. The convention is to use your domain as the unique name with appropriate names for the libraries under that domain. Some companies will take this a step further by having the root domain as the first namespace object, followed by their domain, followed by their library. This leads to the problem of declaring a namespace without overwriting other namespaces. The solution is to use the following way of declaring a namespace:
com = com || {}com.spelchan = com.spelchan || {}
It is a good habit to use namespaces for any script that you are loading. I generally don’t bother with the com root namespace but if Spelchan was a more common word, then it may be a good idea.
Saturday, November 17, 2018
Chapter 5 Overview
Object Oriented programming has become the predominant way of developing software today. While there are some people who believe that object-oriented programming (OOP) is a misstep, there are also people who quite dogmatic about OOP. Both of the Universities I have attended tend to favor the OOP approach but there is starting to be a look at the alternatives to OOP, with functional programming gaining traction. I am a bit more flexible when it comes to programming believing that methodologies, like languages, are tools and one should try and use the appropriate tool for the problem they are dealing with.
Object Oriented approaches are so common, however, that no matter what your stance on OOP is, you should at least be familiar with the concepts behind it. This section of the book takes a look at the core OOP features that JavaScript supports. Unfortunately, like a lot of things about JavaScript, things are not as straightforward as they should be. While JavaScript has always had support for Objects, the language itself did not explicitly support OOP constructs until the ECMAScript 6 standard was released. This means that older browsers do not support the new language keywords but there are a shrinking number of people who use those browsers. It also means that there is a lot of legacy code out there that does OOP the hard way. These techniques are still valid so it is wise to at least be familiar with them.
In this chapter we will look at a number of OOP related concepts looking at what the idea behind the concept is, how ECMAScript 6 supports that concept, and the "old" way of handling the concept.
To start with, in the "NameSpace" section we will be looking at the name space problem and how to get around it. Even if you are not into the other OOP concepts, name spaces are something that ALL non-trivial programs should use.
It goes without saying that objects are important to object-oriented programming so we will then revisit creating objects in "Creating Objects". This will be followed by "Constructing Classes". Classes are what most OOP languages use to define objects with a class keyword being something that was missing from JavaScript until the ECMAScript 6 standard was released.
One of the more powerful, albeit over-abused, features of object oriented programming is the use of inheritance. This allows you to create new classes based off of existing classes without needing to write most of the functionality and will explained in the "Inheritance" section. Polymorphism is how OOP languages take advantage of inheritance by letting you use a child class in place of a parent class. JavaScipt uses a variant of polymorphism known as Duck Typing which gives you a lot of flexibility and power but has some downsides. These issues will be covered in "Polymorphism and Duck Typing".
JavaScript handles classes a bit different from most other object oriented languages. This leads to a strange issue when trying to use an objects function as a call-back. This issue can be solved using something known as binding, which we will explain in a section strangely named "Binding".
What allows JavaScript to bind functions actually is a very powerful construct known as a closure. Closures are a bit confusing and you may not want to use them in your programs, but they are an important JavaScript technique so I will be covering them in the "Closures" section.
And that should cover all the object oriented JavaScript knowledge that you will need for the remainder of this book.
Object Oriented approaches are so common, however, that no matter what your stance on OOP is, you should at least be familiar with the concepts behind it. This section of the book takes a look at the core OOP features that JavaScript supports. Unfortunately, like a lot of things about JavaScript, things are not as straightforward as they should be. While JavaScript has always had support for Objects, the language itself did not explicitly support OOP constructs until the ECMAScript 6 standard was released. This means that older browsers do not support the new language keywords but there are a shrinking number of people who use those browsers. It also means that there is a lot of legacy code out there that does OOP the hard way. These techniques are still valid so it is wise to at least be familiar with them.
In this chapter we will look at a number of OOP related concepts looking at what the idea behind the concept is, how ECMAScript 6 supports that concept, and the "old" way of handling the concept.
To start with, in the "NameSpace" section we will be looking at the name space problem and how to get around it. Even if you are not into the other OOP concepts, name spaces are something that ALL non-trivial programs should use.
It goes without saying that objects are important to object-oriented programming so we will then revisit creating objects in "Creating Objects". This will be followed by "Constructing Classes". Classes are what most OOP languages use to define objects with a class keyword being something that was missing from JavaScript until the ECMAScript 6 standard was released.
One of the more powerful, albeit over-abused, features of object oriented programming is the use of inheritance. This allows you to create new classes based off of existing classes without needing to write most of the functionality and will explained in the "Inheritance" section. Polymorphism is how OOP languages take advantage of inheritance by letting you use a child class in place of a parent class. JavaScipt uses a variant of polymorphism known as Duck Typing which gives you a lot of flexibility and power but has some downsides. These issues will be covered in "Polymorphism and Duck Typing".
JavaScript handles classes a bit different from most other object oriented languages. This leads to a strange issue when trying to use an objects function as a call-back. This issue can be solved using something known as binding, which we will explain in a section strangely named "Binding".
What allows JavaScript to bind functions actually is a very powerful construct known as a closure. Closures are a bit confusing and you may not want to use them in your programs, but they are an important JavaScript technique so I will be covering them in the "Closures" section.
And that should cover all the object oriented JavaScript knowledge that you will need for the remainder of this book.
Saturday, November 3, 2018
Coffee Quest Trilogy Plans
The Coffee Quest (CQ) series started off as an attempt to
create a web-based role-playing game using the then-new Java programming
language. Java, while a very successful language in many areas, failed in the
browser and has been replaced by JavaScript using HTML5 components and CSS3
style sheets. This tends to be collectively called HTML5. My original plans for
porting to HTML5 was to simply use an engine that supported HTML5 and
finally get around to getting my CQFS engine working by building off the back
of another engine. I started experimenting with the Unity engine but really do
not like that engine. Then I remembered that for a game jam I participated in
back in 2012 that I started working on a FPS version of Coffee Quest. That
existing work, while still in an early development stage, would be perfect for
the first three games in the series so I decided I would do a Coffee Quest
Trilogy game that would combine the first three games into a single package. As
this game is developed, the individual episodes will be released on
Spelchan.com giving visitors to my site early access to the game.
The enhanced HTML5 version of the game will be mixed-time
combining both real-time and turn-based play. I may make the option of an FPS
mode where the game would be fully real-time. Each game in the series will have
multiple episodes to reflect the different maps, and possibly a number of new
maps, that will be made available. I have not decided if in the final game to
lock the maps and require players finish other maps to unlock additional maps
or if to make everything accessible. A third option would be to have all the
Coffee Quest 1 maps initially unlocked to the player and once they have
finished any of the maps, to unlock all the Coffee Quest 2 maps. Once a Coffee
Quest 2 map has been completed, then the Coffee Quest 3 maps would be made
available.
My ideal release plans, which knowing history are very
unlikely to work out, are as follows:
Quarter 1 of 2019 would be the release of the Coffee Quest
release candidate with a new map (or two). The goal here would be to have a
better automap, better quest status area, and the ability to click on the
automap to bring up a full-screen map screen.
Quarter 2 of 2019 will hopefully be the beta release of the
Coffee Quest 2 module which introduces monsters and inventory to the game. The
Coffee Quest series uses the concept of conditions to weapons where the weapon
deteriorates in quality over time and will break if the weapon or armor
deteriorates too much. One change that the enhanced version of the game will
have is that picking up additional copies of the same weapon will simply
improve the quality of the weapon if you already have that weapon instead of
filling up slots in an inventory.
Quarter 3 of 2019 will hopefully be the release candidate
version of Coffee Quest 2 with a new map and enhancements/fixes made to the
game.
Quarter 4 of 2019 would be a new Christmas game using the
CQ-Trilogy engine.
Quarter 1 of 2020 would be the beta of the Coffee Quest 3
game which would introduce multi-floor towers.
Quarter 2 of 2020 will be an easter-themed game using the
CQ-Trilogy engine.
Quarter 3 of 2020 will be the final build of the Coffee
Quest 3 module.
Quarter 4 of 2020 will be the beta release of the combined
game and will have some new maps as well as a halloween themed game.
Saturday, October 20, 2018
Site Plans Winter 2018
Working on my Masters of Science in Computer Science is
proving to be very time consuming. While I am trying to allocate blocks of time
towards my porting and site work, the amount of time that I am actually able to
devote to these side tasks is not as much as I would like. This means that
there may be changes to the blog post next year. As the book is mostly written,
there will not be any changes to this blog. The emulator work has slowed down
so I will have to decide how to handle things once I catch up with my current
work. Worst case scenario is that I cut back to monthly updates though will
likely write some side-trip articles covering other aspects of the development
such as the creation of test games and assembly libraries.
Spelchan.com will continue to be updated each month with a
port of an existing game. As you have already seen, the October game was
changed to Pent Up Anger. While I was going to release Coffee Quest 2 next
month, instead I am going to release Cribbage Square and will use my new Coffee
Quest engine to do an enhanced version of Santa’s Search for the December game.
I do plan on releasing a Coffee Quest episode every quarter
of next year, with more information on the Coffee Quest porting plans to be
made next fortnight as next fortnight’s port will be on the making of HTML5
Coffee Quest.
Even though the Dozen Days of Dice series is not overly
popular, I really don’t have time to do a worthy port of One of those Weeks, so
until I have enough time to do a proper OotW port, the Dozen Days series will
be a placeholder with an episode every quarter.
This leaves four games open.
These will probably be used for holiday games but I have not yet made up
my mind what I will be releasing so consider the other four releases for next
year to be mystery games. They may even be new games if I end up creating some
for University assignments.
Next fortnight, as mentioned above, will be a look at my
Coffee Quest port and what the plans are for that series.
Saturday, October 6, 2018
4.8 Winning and Losing
As
you know, the game ends when the last gem has been taken. Also
obvious is the fact that there are two possible outcomes for this
game. The player can win the game, or the computer can win the game.
Knowing this, our movie is going to have to be able to handle either
situation. This will require a bit of JavaScript. Here is the final
version of the gemRemoved function with the new lines bolded.
function
gemRemoved(gemCount) {
if
(gemCount <= gemsRemaining) {
gameMovie.stop();
if
(gemCount == 0) {
if
(gamestate == GAMESTATE_PLAYER_ANIMATING)
gameMovie.gotoAndPlay("playerWins");
else
gameMovie.gotoAndPlay("computerWins");
}
if
(gamestate == GAMESTATE_PLAYER_ANIMATING)
computerTurn();
else
{
gamestate
= GAMESTATE_PLAYER_PLAYING;
}
}
}
Both
sequences start out with the final gem growing larger while moving to
the left side of the screen. This is a simple scaling operation
combined with a movement operation. The end result is shown in the
image below.
To
keep the code simple, the end sequence will simply display the
results for a few seconds before returning to the title screen. An
alternative approach would be to have a button that the user clicks
to return to the title screen. To return to the title screen we
simply call a returnToTitle function at the end of the winning screen
and at the end of the losing screen.
The
line of code that needs to be added to both:
returnToTitle();
Finally,
we need a function in the global script called returnToTitle that
actually does the work:
function
returnToTitle() {
gameStage.gotoAndPlay("Title");
}
And
now we have a our completed NIM game!
Saturday, September 22, 2018
4.7 State of the Game
Progress on the NIM game has gone well and we have the core components of the game implemented (see the earlier sections previously posted). While
we now have the core game mechanics implemented, we do not have a
game. Right now we just have the ability for the player to take gems
unitl there are no more gems. To have a proper game we need to be
able to alternate between two players (the human and the computer).
We also want the user interface to not accept input while the game is
animating or the computer is playing. For this we will need to know
what the movie is doing at any point of time. The easiest way of
tracking this is to have a gamestate variable that holds the current
state of the game with constants defined for the four states that we
are concerned with.
//
explain the different states of the game
var
GAMESTATE_PLAYER_PLAYING = 1;
var
GAMESTATE_PLAYER_ANIMATING = 2;
var
GAMESTATE_COMPUTER_PLAYING = 3;
var
GAMESTATE_COMPUTER_ANIMATING = 4;
var
gamestate = GAMESTATE_PLAYER_PLAYING;
This
means that we can now update the button handling routines to make
sure that it is the player’s turn before doing anything with the
button click. The new code is bolded.
function
handleOneButton(e) {
if
(gamestate != GAMESTATE_PLAYER_PLAYING)
return;
--gemsRemaining;
gamestate
= GAMESTATE_PLAYER_ANIMATING;
gameMovie.play();
}
function
handleTwoButton(e) {
if
(gamestate != GAMESTATE_PLAYER_PLAYING)
return;
gemsRemaining
-= 2;
gamestate
= GAMESTATE_PLAYER_ANIMATING;
gameMovie.play();
}
function
handleThreeButton(e) {
if
(gamestate != GAMESTATE_PLAYER_PLAYING)
return;
gemsRemaining
-= 3;
gamestate
= GAMESTATE_PLAYER_ANIMATING;
gameMovie.play();
}
The
gem removal routine can also be fixed up to nearly it’s final form
by having it start the computer’s turn after the player’s turn
has finished being animated and returning to the control to the
player once the computer has finished it’s turn.
function
gemRemoved(gemCount) {
if
(gemCount <= gemsRemaining) {
gameMovie.stop();
if
(gamestate == GAMESTATE_PLAYER_ANIMATING)
computerTurn();
else
{
gamestate
= GAMESTATE_PLAYER_PLAYING;
}
}
}
Of
course, for there to be a computer player, the computer needs to know
how to play the game. It is quite possible to have the computer play
the game perfectly, but that is not much fun for the player so
instead the computer will play randomly until near the end of the
game where they will take the opportunity to win the game if it
presents itself.
function
computerTurn() {
if
(gemsRemaining <= 3) {
gemsRemaining
= 0;
setMessage("Computer
taking " + gemsRemaining);
}
else {
var
numToPick = Math.floor(Math.random() * 3) + 1;
setMessage("Computer
taking " + numToPick);
gemsRemaining
-= numToPick;
}
Random
number are technically not random, but are pseudo-random but the
details is not important. What had to be understood is that the
Math.random() function returns a number between 0 and up to but not
including 1 so that number needs to be adjusted to a usable value.
Multiplying by three gives us a random number between 0 and just
under 3. We want a decimal number so we use the Math.floor function
to round the number down giving us a number between 0 and 2. Add one
to this and we have the number in the desired range.
This
leaves us with one final issue, which is winning or losing the game. This will be covered next fortnight!
Saturday, September 8, 2018
4.6 Gem Removal
The
game revolves around the removal of gems, so we obviously need a way
of removing gems. As the goal of this movie was to limit the amount
of Action Script that is to be used, we will have to animate the
removal of all the gems by hand. This is not that difficult of a
task, only requiring that we have forty short animation sequences.
The question is how do we stop all forty gems from being removed?
Unfortunately,
this will require a bit of action script at the end of each removal
sequence. This means that each gem removal sequence also must have a
script frame which contains a function call.
gemRemoved(numberOfGemsAtThisPoint);
This
calls a function that handles the removal of gems. The logic here is
simple. We have a common variable that holds the number of gems there
should be. When gems are removed, this value gets reduced. The
gemRemoved handler keeps the movie playing until the number of gems
remaining matches the number of gems displayed on the screen.
As
you develop a game, the code changes as new features are added. It is
always good to follow the KISS (Keep It Super Simple) acronym and
start by writing just the code you need to get the goal you are
currently working on working. As the program develops it becomes more
robust. At this point you can review the code you have and do what is
known as a refactoring pass. This means that you clean up the code so
that it is easier to understand and more maintainable. At this point
we are only concerned about getting gems to be removed so the code in
the remove gems function and the related player panel button handlers
will just contain the code necessary for doing the gem removal. This
code will be changed in the next section as we add the game states
but for now is what is needed to get our gem removal goal completed.
function
handleOneButton(e) {
--gemsRemaining;
gameMovie.play();
}
function
handleTwoButton(e) {
gemsRemaining
-= 2;
gameMovie.play();
}
function
handleThreeButton(e) {
gemsRemaining
-= 3;
gameMovie.play();
}
function
gemRemoved(gemCount) {
if
(gemCount <= gemsRemaining) {
gameMovie.stop();
}
}
For
the gem removal, I just randomly selected a way of removing gems and
manually created that sequence. Before you can remove the gem, you
need to remove the gem from it's gem layer and place a gem in the
same position as the removed gem but on one of the animation layers.
From there, you can freely animate the gem to be removed. I came up
with three separate ways of removing gems. Shrinking, Fading, and
Rockets.
The
shrinking technique simply has the gem shrink into nothing. As
variations on this technique, some rotation can also be applied to
the gem. Essentially you have the first keyframe with the gem at
normal size. You then have the last keyframe with the gem at a
percentage of what it was, optionally rotated. You then have a Motion
Tween between the two frames.
Fading
is simply having the motion tween between the gem with 100% alpha and
the gem with 0% alpha. Rotation can also be done to this, but with
gems near the gem you will want to keep the rotation to only a small
amount.
Finally,
rocket is simply moving the gem from one location to another
off-screen location.
Saturday, August 25, 2018
4.5 Player Panel
At this point in the development of NIM, we are ready to create the panel. This is important as it is the way that the user interacts with the game. As discussed earlier in this chapter, there are other user interfaces that could have been used but this approach was chosen as it is the easiest to implement. The player panel could have been designed as an all in one object, but that would have added a bit of complexity to the design and I wanted to try and keep this movie as simple as possible for those new to Animate CC. Instead, the panel is simply a rectangle with rounded corners. It is animated into its final position by using a simple motion tween.
Once in its final position we add the components on the panel. The first component on the panel is the message bar at the top of the panel. This is a simple dynamic text object labelled “messages” and set to use a center aligned 24-point grey “serif” font. Below that we create a static text message that reads “Take how many?” I broke this into vector text and created a graphical symbol out of the text. Finally there are three buttons labelled “1,” “2,” and “3.” These three buttons are created the same way we created the "Start the Game" button. Simply type in the number. Break apart the number so you get a Flash object. Select the "Make Symbol" option to convert the object into a button symbol. Fill in the over and down keyframes with different colored versions of the number object. Finally, in the hit keyframe, create a solid box that covers the number.
Once all the components of the panel are visible, we need to make the three buttons do something. The code for the buttons is like the code that we used with the start game button. The one significant difference is that we are using a function that handles setting up all three buttons and we also need to be able to turn off the buttons. This code is simple enough as we simply call/remove the handler for each button by using the addEventListener or removeEventListener methods.
function startPanel(gm) {
gameMovie = gm;
gm.messages.text = "You go first!";
gm.one_btn.addEventListener("click", handleOneButton);
gm.two_btn.addEventListener("click", handleTwoButton);
gm.three_btn.addEventListener("click", handleThreeButton);
}
function stopPanel(gm) {
gm.one_btn.removeEventListener("click", handleOneButton);
gm.two_btn.removeEventListener("click", handleTwoButton);
gm.three_btn.removeEventListener("click", handleThreeButton);
}
The actual handlers for when the buttons are clicked are game-play related so will be covered in a later section once we start implementing the gameplay logic. Another function that we will need for the panel is a function to write the message bar text. While this is a very simple thing to do and could be done directly wherever the message needs to be set, I find calling a function that sets the message bar is a bit easier to understand, and adds some flexibility in the case that how messages are displayed changes in the future. Quite simply, this function takes the text passed to it and assigns that text to the dynamic text object we defined in the panel.
function setMessage(s) {
gameMovie.messages.text = s;
}
Once in its final position we add the components on the panel. The first component on the panel is the message bar at the top of the panel. This is a simple dynamic text object labelled “messages” and set to use a center aligned 24-point grey “serif” font. Below that we create a static text message that reads “Take how many?” I broke this into vector text and created a graphical symbol out of the text. Finally there are three buttons labelled “1,” “2,” and “3.” These three buttons are created the same way we created the "Start the Game" button. Simply type in the number. Break apart the number so you get a Flash object. Select the "Make Symbol" option to convert the object into a button symbol. Fill in the over and down keyframes with different colored versions of the number object. Finally, in the hit keyframe, create a solid box that covers the number.
Once all the components of the panel are visible, we need to make the three buttons do something. The code for the buttons is like the code that we used with the start game button. The one significant difference is that we are using a function that handles setting up all three buttons and we also need to be able to turn off the buttons. This code is simple enough as we simply call/remove the handler for each button by using the addEventListener or removeEventListener methods.
function startPanel(gm) {
gameMovie = gm;
gm.messages.text = "You go first!";
gm.one_btn.addEventListener("click", handleOneButton);
gm.two_btn.addEventListener("click", handleTwoButton);
gm.three_btn.addEventListener("click", handleThreeButton);
}
function stopPanel(gm) {
gm.one_btn.removeEventListener("click", handleOneButton);
gm.two_btn.removeEventListener("click", handleTwoButton);
gm.three_btn.removeEventListener("click", handleThreeButton);
}
The actual handlers for when the buttons are clicked are game-play related so will be covered in a later section once we start implementing the gameplay logic. Another function that we will need for the panel is a function to write the message bar text. While this is a very simple thing to do and could be done directly wherever the message needs to be set, I find calling a function that sets the message bar is a bit easier to understand, and adds some flexibility in the case that how messages are displayed changes in the future. Quite simply, this function takes the text passed to it and assigns that text to the dynamic text object we defined in the panel.
function setMessage(s) {
gameMovie.messages.text = s;
}
As the buttons that make up the user interface cover removal of gems, we need a way to remove gems. That will be covered next section.
Saturday, August 11, 2018
4.4 Limiting Layers
In theory Animate lets you have as many layers as you
desire. However, you should try to limit the number of layers that you use to
make managing the movie easier. Designer overload is caused by the designer
having to deal with a huge number of layers. When you have too many layers to
deal with, it becomes harder to find the layer you need and if there is
interaction between layers that are of substantially different levels, then
work can be frustrating.
To layout the game, we need to get the 40 gems to appear. If
you look at screenshots for the game you will see what the final layout looks
like. The problem here is that 40 gems would result in 40 layers. That is a lot
of layers to keep track of. So instead of forty layers, let us group each row
into a layer. Extra conservative designers would probably have all forty gems
in a single layer, but I originally designed this around four layers.
Those of you familiar with Animate CC probably already know
that Animate CC tends to act funny when you try having animation on a layer
that contains more than one object. It wants to only deal with one object for a
tween so when it has many to deal with it automatically generates its own movie
clips to hold intermediate tweens. This is almost never what you want so it is
best to avoid that situation. As that is the case how are we going to animate
the gems? After a bit of thought, I realized that at any given time, I would
only need five gems animated at a time. This can be handled by creating five
animation layers.
The animation is simply handled by assigning gems to
animation layers. When the gem reaches its final position, the layer it belongs
to then adds that gem to it's objects. The animation layer is again free to
hold another gem.
To demonstrate, lets deal with the first few gems. We create
the gems outside the screen in a pattern that wraps clockwise around the
screen. The first gem gets assigned to GemAnim1, which is our first animation
layer. It uses simple position keyframe animation to move (the first keyframe
is where the animation starts, the last keyframe has the gem where the
animation ends and a motion tween is placed between the two keyframes). The
second gem uses GemAnim2, the third uses GemAnim3 and so fourth. Gem number 6
has no layer, but as it will not start to be animated until gem 1 is in it's
final position, it can use GemAnim1. It can do this because there is no longer
an object in this animation layer as a gem has been added to the appropriate gem
layer. Gem 7 is not needed until gem 2 has been placed in it's layer, so it
will use the GemAnim2 layer. And so on, until all forty gems have been
animated.
Subscribe to:
Posts (Atom)