First Project - Lesson 5: Ticker, Mouse Events, and Appearance
Now that we understand threads, spawns, and loops, it is time to learn about a more efficient and easy method to handle timed loops. Your game should try to avoid threads and spawns if possible and only work with the built-in ticker system. This tutorial will cover the built-in ticker system which allows any object to be added to an internal game loop and repeatedly execute code after a short delay. We will go over some ways to handle a game loop with the ticker system and also throw in some mouse events and basics of the appearance system.

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.

Our last tutorial left our project in a bit of a mess, so we will clean it up a bit to start. Open up our previous FirstProject project, go into the main code file and remove the onNew and onLogin code found under our Mob/Player type. The below code is the code you should delete.

onNew() var s = spawn(5000) this.setLoc(1, 1) this.setLoc(this.xCoord + 5, this.yCoord + 5) Event.interruptThread(s) onLogin() var mes = this.myFunction(4, 5, false) alert(mes) var ob1 = {'a': 1, 'b': 2} var ob2 = {'a': 2, 'b': 3} var ob = {'a': ob1, 'b': ob2} foreach (var o in ob) alert(o.a + o.b) Without the need for our custom myFunction function, we can also delete it. Find the below code in the same file and delete it.

function myFunction(pX, pY, pBool) if (!pBool || pX > 3) if (pX !== 3 && pY !== 3) this.setLoc(pX, pY) else if (pX <= 3) this.setLoc(3, 1) else this.setLoc(1, 1) return 'It worked!' else return 'It did not work!' We will start with the ticker system and the basics of game loops. The Vylocity engine has a game loop in the background that always runs, much like a thread from the previous tutorial. The ticker system executes every 10 milliseconds, so the same as thread(10). Each iteration of the loop, it loops through ticker objects and executes that Object's onTick event function and does whatever code is inside of it for that Object. In order for an Object to be added to the ticker, we must call Event.addTicker with the Object we wish to add. Once added, that Object will have it's onTick event function executed every 10ms (unless otherwise specified) until we remove it.

So let's replace our current thread that we gave to our monster with the ticker system. In the main code file find our Mob/Monster code and replace its onNew code and add the onTick event under it like the code below.

Mob Monster onNew() this.dir = 'east' Event.addTicker(this) onTick() if (this.xCoord <= 3) this.dir = 'east' else if (this.xCoord >= 23) this.dir = 'west' this.stepDir(this.dir, 2) So what is our code doing. Under the monster's onNew event we add the monster to the ticker system, then each tick we execute our old thread code which moves the monster east and west. With this system we do not have to create and track a new thread and we also group it with all the other events for the most efficient method possible. There are many more benefits to using the ticker system that we will learn more about below. Build and run the project to see that everything works the same as before.

Our code is doing the exact same thing it was doing with our custom thread (but with simpler and less confusing code), which is good. But now we have more power and control. One of the things that gives us more control is the ticker settings object. The Event.addTicker function takes in an optional second parameter which allows us to give the ticker for our Object some special instructions such as how often onTick is called, how often our tracker resets, and if our Object should be removed from the ticker once it reaches its reset. The last one basically allows us to add the Object to the ticker for a specified amount of time before removing it.

So let's make use of this ticker settings object. Change the monster's onNew function to look like the code below.

Mob Monster onNew() this.dir = 'east' Event.addTicker(this, {'delay': 100}) The above code adds the {'delay': 100} object to our Event.addTicker call. Delay is the amount of ticks to wait before executing the Object's onTick event. We set it to 100 which means 99 ticks will happen without anything happening to our monster, then on the 100th tick, we execute our code. Because the internal loop runs at 10ms, 100 ticks would be 1000ms (add a zero) or 1 second. So every second our monster makes one step. Build and run to see it in action.

Before we take a look at the other values the ticker settings object can have, we will learn about two more event functions involved with the ticker system. When an Object is added to the ticker it has its onTickerAdd event function called and when it is removed it has its onTickerRemove event function called. These events allow us to do special things at the start and end of our action loops. For the sake of testing these event functions, we will learn a variable involved with the appearance system early (we will cover other variables and information on the appearance system later in the tutorial). That variable is the color variable. Each Diob has a color variable which allows us to change the color of the icon that belongs to the Diob without making a new icon. Assuming you did not make the color of your monster's icon just the color red, we should be able to tell when we set our monster's color to red and back again as we add and remove it from the ticker.

Add the two following event functions to the Mob/Monster type.

Mob Monster onTickerAdd() this.color = 'rgb(100, 0, 0)' onTickerRemove() this.color = '' The above code will overlay a solid red color over our monster's icon when it is added to the ticker, then remove the red once it is removed from the ticker. The color variable can take in rgb (red, green, blue values from 0-255, where 0 is black and 255 is white), hex codes, or a color matrix (this requires its own tutorial).

If we build and run the project now, our monsters will just be a solid red color. In order to see the color change, we will need to remove the monster from the ticker so that it reverts back to normal. So let's change our ticker settings object so that our monster's ticker tracker resets and removes from the ticker. Change our Event.addTicker call to be Event.addTicker(this, {'resetAt': 500, 'resetEnd': true}) so that our monster's onNew function looks like the code below.

Mob Monster onNew() this.dir = 'east' Event.addTicker(this, {'resetAt': 500, 'resetEnd': true}) This will have the internal ticker counter (each internal tick will increase the counter) reset after 500 ticks. So with each tick being 10ms, after 5 seconds our counter will reset. With our new resetEnd value set, instead of only resetting, our ticker will end (the monster will be removed from the ticker as if Event.removeTicker was called). So our monster will be removed after 5 seconds and should stop moving and our new onTickerRemove event should be called, which removes the red color. Build, run, and see for yourself.

When getting down to the basics of how a game works, a game loop is at the core. Especially for the AI and other automated events that make the game a game. Even the player's actions can be interpreted by the game loop in order for the action to happen. Our current setup only allows us to do a block of code every 10ms (or whatever delay we specify) but games can get more complex. An example would be if our monster had different attacks or actions that happen at varying rates of delay. The best solution to this problem is to have our ticker execute at the lowest possible speed (if we have code that needs executed every 1000ms and then every 5000ms, 1000ms is the smallest amount and covers both cases so our delay can be 100 and the 5000ms block will execute after 5 onTick calls) and then use if statements to check how much time has passed since last reset or last execution of our if statement so that different code executes at different times.

In order to handle our different executions for the same monster, we will need to make use of the argument passed into the onTick event function that tells us what the ticker tracker count is at. Each time the internal ticker fires and our Object is in the ticker, this value is increased by one. We can use this variable to determine what action we should take. Change our Mob/Monster's onTick event function code to look like what is below.

Mob Monster 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) We have three new lines here, and they are pretty complex compared to what we have been working with, so let's break it down as best as possible. Do not worry about understanding setTransition too much right now, it's a pretty complex function with lots of options which will be covered better in a future tutorial. But the basic idea of the function is it allows you to transition a variable over time from one value to another (in this case, a very smooth animation of scale). There are a few appearance variables that you are allowed to pass into this function in order to transition it from one value to another, but in our case we are making use of scale, which is basically making our monster go from normal size to double its visual size (the icon) over 500ms. Our second setTransition returns the scale back to 1 (normal) over another 500ms. The last parameter of our second setTransition is the queue toggle, which basically means add this transition to a queue to be executed after any current transitions are complete. You might also notice that our onTick event now has an argument called pT which holds our ticker counter.

In order for our setTransition codes to be reached, it has to get past our if statement. So what is happening in if (!(pT % 150))? Well, the % operation gives you the remainder of division between the two numbers surrounding it. So 390 % 100 would be 90 for example. We divide our monster's ticker count by 150 and then check if the remainder is 0, which means 150 divides into our current counter perfectly, so 150 ticks have happened (1500ms). Breaking everything down, every 1500ms our monster's scale will go to 2 over 500ms then back to 1 over another 500ms (for 500ms it will stay normal before going to 2 again).

There are a couple things to consider with what we have created here. Because we set our resetAt ticker count reset to 500, our third scale (three 150s) would happen at 450 and our counter would reset at 500 (assuming we didn't have resetEnd set) so 50ms of tracking would be lost. Also, the first onTick call will have pT set to 1, which has a remainder when divided with 150, so the first tick when the ticker starts will not execute anything. So if you want the code to execute when the ticker starts and then every 150ms after, you will need to subtract 1 from pT so that the first tick is 0 and it makes its way through our if statement. Or you could put the code into a custom function and call it under the if statement as well as in onTickerAdd, whatever works best for you.

Build and run the project to see our creation in action.

While on the subject of the ticker, let's give our World a ticker that we can use for general purposes.

We will find out more about this over time, but the World static object has many useful functions we can call to interact with the world. One of those functions we will need to know about for this next code is the getDiobs function, which returns an array of every Diob in the world. We will go over it a bit more below.

Find our World type in the code and give it the events in the code below.

World onNew() Event.addTicker(this) onTick(pT) if (pT === 1) foreach (var d in World.getDiobs('Mob', true)) d.text = 'hello' else if (pT === 50) foreach (var d in World.getDiobs('Mob', true)) d.text = '' We are introduced to two new things here (one previously mentioned). World/getDiobs gives us an array (a list) of all diobs in the world, but because we passed in two parameters, we narrow down what it should return a bit. The first parameter is the type to look for. We put 'Mob' for our type. Normally this would mean only return diobs that have the exact type of 'Mob' but we also put true for the second parameter, which means include child types as well, so anything that starts with 'Mob' (including our 'Mob/Monster' type) will be returned. Then our foreach loops each thing in the array returned by our getDiobs call, referencing each diob with the variable d, which we use to set the text of it to equal the 'hello' string. This is all assuming our pT argument is equal to 1 (the first call). So what does text do? Well it is another appearance variable which displays text above the diob on the map. In our case, each 'Mob' in the game will have the word 'hello' drawn on it. Our second block of code basically clears the text of every Mob in the game after 500ms of having it set (50 ticks).

Note: World/getDiobs is pretty expensive because it looks through every diob in the game and your game could have hundreds of thousands of them, or more. Doing this a lot every second could slow down the game. There are most likely better solutions for anything you are trying to implement that is more efficient than using World/getDiobs. Some of those things will be learned in future tutorials.

The amount of things you can accomplish with the ticker system is infinite. Majority of your automated game logic will probably happen inside of the ticker system.

Most other code will probably happen from built-in engine events like mouse events (for games being played on the computer), among other things. So let's take a look at how to manage mouse events.

There are many mouse event functions. We will list them all here and give a brief description, but we will only make use of a few of them.

Let us start by applying a few new appearance variables to our player as we mouse over it and then return the values back to the defaults after we mouse off of it. Add the following event functions and code to our Mob/Player type code.

Mob Player onMouseEnter() this.scale = 2 this.alpha = 0.5 this.angle = 0.5 onMouseExit() this.scale = 1 this.alpha = 1 this.angle = 0 Nothing too complex is happening here, but let's go over it. When a client (you) mouses over a Mob/Player Diob, onMouseEnter is executed, which sets the diob's scale to 2, alpha to 0.5, and angle to 0.5. Then when the client moves their cursor off of the Diob, onMouseExit is executed and scale returns to 1, alpha to 1, and angle to 0.

We introduced two new appearance variables called alpha and angle. We can change how transparent our Diob's icon is with the alpha variable. It is a value between 0 and 1 where 0 is completely transparent (it cannot be visually seen) and 1 (the default) is completely visible. We set it to 0.5 which is 50% visible. Then we can rotate our icon with the angle variable (which is a value in radians). We set it to 0.5 which is about 28.6 degrees.

Both mouse event functions we just learned have three arguments that gives us additional information to work with. The first argument is a reference to the client that triggered the mouse event (which will be you, since you're the only one playing), the second is the x position over the Diob the mouse interacted at, and the third is the y position over the Diob the mouse interact at.

Most of the mouse events have similar arguments, but let's make use of the ones that belong to onMouseMove and add the following mouse event function to our Mob/Player type code.

Mob Player onMouseMove(pClient, pX, pY) this.setAnchor(pX / this.width, pY / this.height) We are introduced to a new appearance variable here called anchor, which is our pivot point where all our transformations (scale, angle, so on) and such happen. The default value is 0.5, which means everything happens from the center of our icon. Our code grabs the x and y values passed into the event function (named pX and pY respectively) and gets the percentage of the icon that it covers. Origin of everything starts from the top-left corner, so an anchor of 0 means top-left and an achor of 1 means bottom-right. Our x and y values also start from the top-left as well, so mousing over the Diob in the top-left corner would return 0 and mousing over the bottom-right could return 31 (because our size is 32x32 and we start at 0, so 31 is the max).

There are many more mouse event functions left to talk about, but we will only test out one last one called onMouseClick. Before we do so, here is a list of the rest that were not covered here, and a brief description of what they handle.


Diob/onMouseDblClick() is executed when double clicking the Diob, Diob/onMouseDown() is executed when the mouse is held down on the Diob, Diob/onMouseUp() is executed when the mouse is released over the Diob, Diob/onMouseWheelScrollUp() is executed when the mouse scroll wheel is scrolled upward while over the Diob, and Diob/onMouseWheelScrollDown() is executed when the mouse scroll wheel is scrolled downward while over the Diob.

So let's add a onMouseClick mouse event to our Mob/Player type code that just simply executes code we've seen before.

Mob Player onMouseClick() this.setLoc(1, 1) With this, any time we click on our player mob it will return to 1,1 coordinates on the map. Feel free to build and run to test it out.

Now that we know about all of the mouse event functions, it is time to learn about the Client versions of them. Every mouse interaction in the game will trigger the proper mouse event for a Client, even if no Diob is involved. The Client and all Diobs have the same mouse event functions, but the Client versions have different arguments.

We wont go over all of the mouse event functions again, but we will add a Client version of onMouseDown just to see it in action and get another chance to use setTransition. Anywhere in the main code file enter the code found below. Just do not interrupt any of our other type code. If you are worried about doing that, the safest bet is to add a new line at the top or bottom of the file and enter it there.

Client onMouseDown(pD) if (!pD) return pD.setTransition({'angle': Math.PI}, -1, 1000) pD.setTransition({'angle': 0}, -1, 1000, true) Now any time we mouse down (first part of a click) on a Diob, it will rotate to 180 degrees (Math.PI is a static variable that is equal to the value of pi, which is ~3.14 radians or half a circle) and then back to 0. The purpose of the first line of code might stick out (along with the fact that you can put a single line of code after something like an if statement). What we do in the first line is check to see if the argument pD has any value (meaning it is set to something). An if statement can return true if you only check a variable without any expressions and it has a value that is set and not 0, null, or undefined. Because of this fact, we check if pD is set and if it's not we exit the event function to avoid errors. Our Client mouse events can be triggered even if we aren't interacting with a Diob, which means pD can be undefined, so we need to make sure it is not undefined before trying to use a function that is unable to be called.

Build and run the project to see the code in action.

There are two more things that go along with the mouse system which we will talk about but not make use of.

Every Diob has a mouseOpacity variable which determines how the mouse interacts with it. The default value of the variable is 2, which means the mouse interacts with the physical box of the Diob. If this variable was set to 1, it would use the icon as the mouse interaction, but because both our physical box and our visual icon should match up at 32x32, this variable setting does not really matter to us right now. However, if we set it to 0, no mouse events would be triggered on the Diob. This could allow you to have something visual but not take up the mouse interaction when it is above something else.

The last thing that goes with the mouse system is of course the ability to change the mouse cursor. For the purpose of testing, we will throw some code in to make our cursor look like our player. Add the following code to our Client type code.

Client onConnect() this.setMouseCursor(this.mob) We have a new event function here called onConnect, which basically calls when a client first connects to the world. We use it for the purpose of setting our cursor as soon as possible, but not before our player has been created and given an icon (using Client/onNew() would mean we do not have a player yet).

So, we have now learned how to use the ticker system, how to manage mouse events, and learned a bit about some Diob variables associated with the appearance system. The next tutorial will go more into depth on Object types, inheritances, and static objects.

This tutorial is fairly packed, so if you had any issues or need to double check anything, be sure to check out the source below.

If you followed everything in this tutorial, the source of your project should look something like this. https://vylocity.com/ide/Vylocity/FirstProject5/

References