We have learned quite a bit about Object types and how to define variables and functions under them, as well as how to create child types (Mob/Player for example). In this tutorial we will focus a bit more on how inheritance works for Object types and how we can manipulate it to fit our needs. We will also introduce a few new static objects that the engine has built-in and go further into detail about some we have already used. Some built-in function variables (behind the scenes variables that every function has that can be used to access information about the function call) will also be introduced.
We will continue from the project we created in the previous tutorials. If you are just now jumping in, go to the previous tutorial and create a project to match the project at the bottom of the page.
Open up our previous
FirstProject project in the IDE and open the
main code file. If it has been awhile since completing the last tutorial, be sure to look over the code to get yourself familiarized with it.
As we learned in a previous tutorial, Object types have an inheritance system which allows child types to get variables and functions defined under their parent types. If you have any experience in programming, a type is much like a
class in other languages.
The lowest level of inheritances (the type that all other types inherit from) is the Object type. The Object type has very few properties and is designed to hold data and track information (much like just a basic object, but with a few extra properties and the ability to use the type system). It does not have the necessary properties needed to be displayed, so the Diob type exists for this reason. Anything that is seen on the screen will have to inherit from Diob. We were shown a list of built-in types that inherit from Diob in a previous tutorial, but we will list them again as a refresher.
All of the above types will inherit from Diob, but we can also create our own. We learned about
Mob and
Tile already.
Movable is
Mob's parent type, which gives it the ability to move around on the map. The rest of the types will be explained more in future tutorials.
Custom inheritances allow us to give our custom type a parent without adding that parent to the our type string. This can simplify our type tree and use smaller type strings, among other things. The best way to understand this more is to just get it into our project! Inside of the
main code file, we are going to be making some alterations to our
Mob/Player and
Mob/Monster type definition codes. Change that code to look like the following.
Note: You can back tab large sections of code by selecting it and then holding shift while pressing tab.
Mob
atlasName = 'icons'
Player : inherit [Mob]
iconName = 'player'
xCoord = 5
yCoord = 5
onMouseEnter()
this.scale = 2
this.alpha = 0.5
this.angle = 0.5
onMouseExit()
this.scale = 1
this.alpha = 1
this.angle = 0
onMouseMove(pClient, pX, pY)
this.setAnchor(pX / this.width, pY / this.height)
onMouseClick()
this.setLoc(1, 1)
Monster : inherit [Mob]
iconName = 'monster'
width = 64
height = 64
onNew()
this.dir = 'east'
Event.addTicker(this, {'resetAt': 500, 'resetEnd': true})
onTick(pT)
if (this.xCoord <= 3)
this.dir = 'east'
else if (this.xCoord >= 23)
this.dir = 'west'
this.stepDir(this.dir, 2)
if (!(pT % 150))
this.setTransition({'scale': 2}, -1, 500)
this.setTransition({'scale': 1}, -1, 500, true)
onTickerAdd()
this.color = 'rgb(200, 0, 0)'
onTickerRemove()
this.color = ''
Basically we back tabbed all our player and monster codes and added
: inherit [Mob] to both types. It does not seem like we did much, but we turned our
Mob/Player type into just
Player and our
Mob/Monster type into just
Monster, which are shorter. One could also argue that the code looks cleaner and more compact. Once a project gets larger and type trees get longer and more complex, things like this come in handy. These are only some of the benefits of this inheritance system.
Before we build and run, we need to make sure we update our
World/mainMob to point to our new player type. Find that code and update it to look like below.
World
mainMob = 'Player'
This next step is going to be a new learning experience about the IDE and map editor. Our map contains our old types, so after we build and open the map, it is going to yell at us that
Mob/Monster does not exist. We need to fix this issue. When you open the
map map file after building, you should get an alert. Simply replace
Mob/Monster with
Monster and hit OK. Build, open
map, replace the types, then build again and run the game and everything should work as normal.
The inherit system allows more than one type to be inherited at the same time (the order of them matters). So let's learn a bit about that before moving on.
We will create a new Object type that will hold some information for us that we can give to whatever other type we want with the inherit system. Above our
Mob type (on a blank line and not inside any other code) add the following code.
Prop
var myVar1 = 1
var myVar2 = 2
Prop is the name of our new custom Object type (it is not built in) and it has two variables.
We can now give these two variables to our
Player type (or any other type we want). Go to our
Player type definition which currently looks like
Player : inherit [Mob] and make it look like below. Also add the
onNew event function shown to the type.
Player : inherit [Mob, Prop]
onNew()
alert(this.myVar2)
Now our
Player will inherit from
Mob and then
Prop. You can add as many more types as you want. They will be inherited in order from left to right.
Build and run and we should now get a
2 alert at the start of the game, since our player's
myVar2 variable is being outputted and it is equal to 2.
Moving on. So we have our normal
Mob type definition without
atlasName defined under it and our
Player and
Monster types which both inherit from
Mob (giving them the atlasName). The next thing we will learn about is the
override operation, but before we do, we need to set things up and see why we might want to use it.
Under the
Mob type, add a new
onNew event function like we had for our old
Mob/Player type in previous tutorials. Change the
Mob type code to look like below.
Mob
atlasName = 'icons'
onNew()
this.setLoc(10, 10)
This code should be familiar and not need explanation again. Build and run the project. All of our monsters, as well as us, should now all be stacked together at the 10,10 coordinates (the monsters will move together from the location immediately).
We probably do not want our monsters to execute this code, so we need to override it. Note: The obvious solution here is to add the
onNew event to
Player so that
Monster never gets it, but we are learning inheritance manipulation here!
So let's override the
onNew event function for
Monster. Simply add
override to the top of our
onNew event function found under our
Monster type like shown below.
Monster : inherit [Mob]
onNew()
override
this.dir = 'east'
Event.addTicker(this, {'resetAt': 500, 'resetEnd': true})
Build and run the game and our monsters should now no longer be at the 10,10 coordinates. Our
override operation has blocked any inherited
onNew code from being executed.
A couple things to take note of when using
override. You must put
override at the very top of the function as the very first line, every time. Also,
override will not prevent child code from being executed. For example, if a new type was added that inherited from
Monster, the
Monster onNew that starts with the
override would execute first, then the child's
onNew would call.
Before we move on with inheritances, we need to learn about a new global function that the engine that lets us create brand new diobs during runtime. But before we learn that, lets add a new type definition for us to create (you can create any type during runtime, but this will let us use something we have not used yet). Above the new
Prop type (the same was it was added itself) add the type below, along with it's properties.
Movable
atlasName = 'icons'
iconName = 'monster'
Mob inherits from Movable, so it will not inherit anything we have already created for our Mob, Player, and Monster types. But because Mob does inherit Movable, anything we add to Movable will be given to our Mob types. This means our
atlasName = 'icons' defined under
Mob is kind of useless now, but we wont worry about it. We gave the new type the monster icon just for simplicity.
Now that we have a new type with properties that we can create, lets learn how to do that. Find our
Tile type definition code and put the following event function and new variable code found below under it (make sure the code is indented under
Tile and not the
Tile/Dirt or
Tile/Grass types).
Tile
mouseOpacity = 2
onMouseDown()
var d = new Diob('Movable')
d.setLoc(this)
A couple new things here. Tiles have
mouseOpacity set to 0 by default, so in order to be able to interact with them, we need to give them mouse opacity. The
new Diob('Movable') code allows us to create a new diob with the specified type during runtime. This is a very important feature and will be use
a lot while making your game, so it is good to know (if you wanted to create a different type, you would simply replace 'Movable' with the type). Then of course
var d holds a reference to that new diob so that we can work with it, which we do in the next line. The
setLoc call should be familiar by now, but this time instead of passing in coordinates, we pass in
this alone (which points to our tile). We are able to do this because tiles are locs. Each loc (location or coordinate) has a tile, so they are related.
If you build and run the game, you can click on tiles on the map and place new
Movable diobs.
Note: These new diobs look like our monsters, but they are different types, so they will not move or use a ticker like our monsters do.
Before we proceed, let's clean up a bit. We will be using alerts to display some information, show inheritance orders, and other things. So we should remove our current alert located under our player's
onNew event function. Find that code and remove the two lines associated with it. It should look like the code below.
onNew()
alert(this.myVar2)
With that gone, we can continue. We will look into some of the built-in function variables that we have access to. The first is the
arguments variable. Any function can access this variable and it will provide useful information about the function call, including an array of the arguments being used. Lets add a child type to our
Movable type, give it an
onNew event function, and loop the
arguments variable to output the value of each argument. Change your
Movable type definition code to look like the code below.
Movable
atlasName = 'icons'
iconName = 'monster'
Test
onNew()
foreach (var v in arguments)
alert(v)
If we ran this code right now, we would not get any alerts because we do not currently have any arguments. With that said, any function can be called with any amount of arguments, even if the function code does not give them names or define them. So that makes it easier for us to add something to alert.
Find our
new Diob('Movable') code under the
Tile onMouseDown event function and change it to be
new Diob('Movable/Test', 1, 2) so that the whole block of code looks like below.
Tile
onMouseDown()
var d = new Diob('Movable/Test', 1, 2)
d.setLoc(this)
Every parameter passed into
new Diob after the type string will become arguments inside of that new diob's
onNew event function. So now when we build and run the project and click a tile, we should get two alerts which display
1 and then
2.
Note: You may notice that once the
alert appears, everything behind it freezes. This is good lesson to learn. You should not use
alert in your game unless you are using it for quick testing.
The
arguments variable holds other information other than just a list of the arguments, but we wont go into those details in this tutorial.
Up next is the
retVal function variable. This variable holds the return value from a parent's function call.
In order to see this in action, we will add an
onNew event function to
Movable that returns a value and then output that value under our
Movable/Test's
onNew event function. Change the
Movable type code to look like the code below.
Movable
atlasName = 'icons'
iconName = 'monster'
onNew()
return 12
Test
onNew()
alert(retVal)
Build and run the code and when you click on a tile, an alert with
12 in it should appear. This code allows us to pass a value from a parent function call to the child function calls, all the way down if desired.
One last thing to cover related to inheriting, which also overlaps with static objects that we will be covering more shortly, is the ability to go back and call parent functions (or any function really) when using the
override system. This gives us the ability to change the order of our function calls if we want to.
In order to do this, we will need to be introduced to a static object function called
Type.callFunction. This function allows us to call any function that belongs to any type, and do so with different arguments and even a completely different owner (the
this inside the function). Having control over the
this of the function is important, because if we call a function that belongs to
Mob/A by a diob that has the type
Tile/A we want the
this of that function call to be the diob that called the function and not a diob that has that function defined.
To see this in action, we will have the
Movable/Test type
onNew use
override and then call the parent
onNew at the end, essentially reversing the order that they are called in. Change the
Movable type code to look like below.
Movable
atlasName = 'icons'
iconName = 'monster'
onNew()
foreach (var v in arguments)
alert(v)
Test
onNew()
override
alert(3)
Type.callFunction(this.parentType, 'onNew', this, arguments)
Normally when running this code (without an
override and
Type.callFunction call) we would get three alerts in order from 1 to 3, but with our
override we prevent
Movable's
onNew from being called first and get a 3 then 1 and 2. The
Type.callFunction function has four parameters, which we use above. The first is a string with the Object type that owns the function we want to call, the second is a string with the name of the function, the third is a reference to what object should be the owner of the call (the
this inside of it), and the fourth is an array of arguments to pass into the function. For the first parameter we use
this.parentType which is a new variable for us, but it holds a string that is the parent's type which is
Movable in this case. For the second parameter we use the string
'onNew' because that is the name of our function, then we pass in our
this and
arguments so that they are the same as our current function.
Go ahead and build and run to see the code in action.
That basically does it for the inheritance system. We will now go over some of the built-in static objects and some of their variables and functions. We just used the
Type static object above, so we will continue to explore it some more.
The
Type static object holds important information relating to our project's Object type system, along with useful functions for being able to manipulate types or gather information on them during runtime. Two of the most important functions the static object has is probably
Type.callFunction and
Type.getVariable. We already saw the first, so we'll try out the second before moving on to the next static object. If you want to see more properties that the
Type static objects has, you can visit the
documentation page.
To make use of
Type.getVariable we will make some changes to our code. What
Type.getVariable does is it allows us to get the default value of a variable belonging to the specified type, instead of the current value of a variable belonging to an active Object.
In our code, under the
Tile type code that we created awhile back in a previous tutorial, we will give the
Tile/Grass and
Tile/Dirt types two more event functions which will return their icons back to normal after we exit them. Find that code and make it something like the code below.
Tile
Grass
iconName = 'grass'
onEntered()
this.setIcon('icons', 'dirt')
onExited()
this.iconName = Type.getVariable(this.type, 'iconName')
Dirt
iconName = 'dirt'
onEntered()
this.setIcon('icons', 'grass')
onExited()
this.iconName = Type.getVariable(this.type, 'iconName')
Our code returns each
Tile's
iconName back to the default of
'grass' or
'dirt' after the thing entering it leaves. We also use a new variable at
this.type which is just a string holding the type of our Object. This
Type.getVariable function is useful for returning values back to normal or default like this.
Build and run and see how each tile you enter has its icon change as you enter and then return back to normal as you leave.
Another useful and very commonly used static object is the
Map static object. It is a static object you will be using quite a bit to work with things on the map, which is a large part of a game. There are way too many properties to cover here, but we will go over a few commonly used ones.
The first function we will learn about is the
Map.getRange function. This function will give us an array of all non-tile diobs on the map around the diob we pass into it, with a few variations we can enable. The first parameter of the function is where to start to look for diobs, the second is the number of positional units to the sides of that diob to look, the third is the number of positional units above and below the diob to look, and the fourth is if the diob itself should be included in the returned array.
We will use this function inside of a new event function called
onRelocated (which is called every time a diob changes position) that we will put under the
Movable/Test type. In order to test this properly, we need to clean up some of our code under the
Movable type and get rid of the alerts, so make that code look like the code below.
Movable
atlasName = 'icons'
iconName = 'monster'
Test
onRelocated()
foreach (var d in Map.getRange(this, 50, 50, true))
d.scale = 0.5
At this point, the code should be somewhat familiar. Basically when a
Movable/Test Diob moves in any way (including when it is first placed down on the map or changes maps), it will grab all diobs that are up to 50 positional units away from our
Movable/Test Diob (minus the diob itself) that we placed down and loop them, setting each one to have have a
scale of 0.5.
Build and run and click some tiles to put
Movable/Test diobs on the map and notice when you put one down close to another, it shrinks the ones around it.
You may have noticed that this code can also shrink our player or the monsters, which might not be what we want. We could use an if statement to weed out those things, but there is also a built-in
Map function to handle it for us called
Map.getRangeByType. The function is the same as
Map.getRange but it has two new parameters after the first which can be set to only grab diobs with a certain type or parent type. We saw a version of this when we used
World.getDiobs in a previous tutorial.
So lets change our code to use that function. Make the
Movable/Test onRelocated function look like the code below.
Movable
Test
onRelocated()
foreach (var d in Map.getRangeByType(this, 'Movable', true, 50, 50, true))
d.scale = 0.5
Now our code should only give is diobs that have the
Movable type or has a parent type that starts with
Movable. Build and run to test it out.
There are quite a few more functions belonging to the
Map static object which are related to these two functions, but we wont be going over them. If you are interested in some of them, you can check out the
documentation page for the
Map static object.
One last
Map function that will probably be commonly used is the
Map.getDist function, which returns the positional unit distance between two diobs on the map.
Find our
Client type code and change the
onMouseDown event function code to be like the code below.
Client
onMouseDown(pD)
if (pD)
alert(Map.getDist(this.mob, pD))
Each time you press the mouse down, it should alert with the positional unit distance from the center of your player mob to center of the diob clicked on. The
this.mob variable is a
Client variable that references the client's player mob.
Build and run and see how the number increases the further away you click from your player mob (the center of the screen).
The final static object we will be covering in this tutorial is the
Util static object, which is basically just a static object containing properties that could not find a home somewhere else. There are quite a few things and we will only be covering a couple, so if you want to learn more about the properties the
Util static object has, go check out the
documentation page for it.
First up is the
Util.toRadians function. There is also a
Util.toDegrees but because we need radians for angle, we will use degrees that are converted to radians for simplified angle setting. Find the
this.angle = 0.5 code under our
Player type in the
onMouseEnter event function and change it to
this.angle = Util.toRadians(35) so that the event function looks something like the code below.
Player : inherit [Mob, Prop]
onMouseEnter()
this.scale = 2
this.alpha = 0.5
this.angle = Util.toRadians(35)
Now we can use degrees (35) as an angle. You can build and run to see the code still works properly with the new function call.
One last
Util function that might be useful is the
Util.pick function. We can either pass an array into it or a bunch of parameters and it will return only one value, basically picking the value for us.
To test, we will replace the
Client's
onMouseDown event function again. Make it look like the code below.
Client
onMouseDown()
alert(Util.pick(1, 2, 3, 4, 5))
Build and run and each time you click you should get an alert that displays 1 to 5 randomly.
We have learned a bit about the Event, World, Map, Type, and Util static objects. We could probably spend days going over all the possible static objects and all of their properties, but it is good to understand that they all exist and go over a few of the commonly used ones. There are quite a few more static objects that we did not go over. Here is a list some of the other available static objects.
So, we have now learned how to use the inheritance system, a bit about some static objects, and also about some built-in function variables. In the next tutorial we will dive deeper into strings and arrays.
If you followed everything in this tutorial, the source of your project should look something like this.
https://vylocity.com/ide/Vylocity/FirstProject6/
References