Your First HSL Script
- This is an intermediate-level tutorial on how to write an HSL script that uses class methods, which will cause a visible reaction to an object on the screen when the mouse is moved
Before working through this tutorial, you should have worked through or be familiar with:
- How to write a script
- Creating server scripts
- Easy-level tutorial, explaining how to use the Script Editor to create a server-side script
- Using the DOM Editor to create a new class
In this tutorial you will:
- Create an object class via the DOM
- Learn about mouse events
- Write code to make an object on the screen move when the mouse pointer moves over it
Assume you want to create an object that will move away from the mouse when a user moves the mouse pointer over the object. This is an easy task with HeroEngine, but as they say, "the devil is in the details..."
This tutorial will walk you through creating a class in the client DOM that will receive mouse events and cause an object which has the class GLOMmed onto it to move when the OnMouseEnter event occurs. When our event occurs, our object will move away from its current position by a random offset along the x/z axis.
What problems does this tutorial address?
- Receiving a Mouse Event on an Object on the Client
- Repositioning a Client Object
- Changing the Diffuse and Ambient Light on an Object
What problems does this tutorial not address?
- Communicating to clients that an object has these behaviors
- Persistence of the new behaviors
- Does not deal with replicating the property changes to other clients in the area. (i.e. Only you see the object moving)
Mouse Events are generated by the input system
Mouse events are generated by the C++ engine and passed into the input system, passing through whatever input layers are defined by your game. By default, the Clean Engine's mouse events enter the _Input_Mouse script which makes calls to the $INPUT system node.
Important: The HSL Input System only receives notification when you are in Character mode, either in HeroBlade by selecting the character mode button or by virtue of connecting using the (non-development) player client.
The class methods script _scriptingTutorial1 serves as a code example for this tutorial. Included in it are two different ways of transforming the object's position: One does it as an immediate update, and the other way transforms the object's position/rotation/scale to the desired target values over time, using the _transform3d class.
The steps for implementation are:
- Create a Client Class
- Create a Class Method Script
- Add An Instance From the Library
- Add Your Class to the Instance
- Writing Code For Your Class Method Script
Create a Client Class
Using the DOM Editor, create a new client class with a name that is <yourName> + ScriptingTutorial1 (ex. ChristopherScriptingTutorial1). For this particular tutorial you will not need any member fields for your new class.
Create a Class Method script
Using the HeroScript Editor, create a new empty client script and name it <yourName> + ScriptingTutorial1ClassMethods (ex. ChristopherScriptingTutorial1ClassMethods).
Add an Instance from the LibraryLibrary, add a box (or sphere if you prefer) near your camera by double-clicking on it.
You can use any normal asset you like, but the white box comes with Clean Engine.
Important: Make sure to select the Instance of the asset and use the Properties panel to set the MouseTargetable property to true. This tells the input system that this object can receive mouse events. If you do not set MouseTargetable to true, you will only get the mouse events when you have the node selected with one of HeroBlade's selection tools.
You also need to verify that the Moveable property is also set to true for this instanced asset.
Add your Class to the Instance
Select the instance of the white box that you created and open the HeroBlade's Properties Panel, you will need to copy the GUID of the instance for use with a CLI command to add your class to the instance.
Once you have copied the GUID's ID, use the following command in the Console window.
/mnac <GUID>, <YourClassName>
ex. /mnac 94870044549, ChristopherScriptingTutorial1
MNAC stands for Modify Node, Add Class. This command will add the new class you just created to the existing node. Remember that in HeroEngine, a node is an instantiation of a class and a node can have any other class added to it at any time. In this way a node can dynamically change what it is (through aggregation of new classes) at run-time. On the client, all asset instances (such as the white box you just created) are backed up by an HSL node. And by adding your new class to this node, we can now implement some methods to respond to mouse events for it.
We are now ready to implement the behaviors for our object.
Writing code for your Class Method Script
Your class method script will need to do these four tasks:
- Catch the Mouse Event and Do Something
- Get a Random Positional Offset
- Reposition the Object
- Set the Object's Diffuse and Ambient Colors to a random Color (just because it looks cool)
It is intended that you copy and paste the sample code into your class methods script. Remember to submit the new script after it is compiled. Take the time to try understand what the code is doing, once you have this sample running use the exercises to expand what the code does to manipulate different or additional Properties.
Catching a Mouse Event
Assuming you have not overridden the Clean Engine implementation of the Input System, the _InputHandlerClassMethods script makes a callback to a shared function InputMouseEvent. Using the args passed in, we can identify the node to which the event applies and modify its parameters (position, diffuse and ambient colors)
Having received an event and identified the node for which the event was generated, we can make use of the node's Properties to manipulate the node in a variety of ways (including the position).
shared function InputMouseEvent(args references Class _MouseEvent) // Mouse event generated from the _InputHandlerClassMethods Script assuming it // has not been overridden by a game specific implementation. // // Get the Node For which the Event was Generated target as NodeRef = args.MouseTarget // Type Cast the node so we can access its fields/properties where target is kindof HBNode when args.MouseEventType is Enter target._scriptingTutorial1SimpleReposition() // Change the Ambient and Diffuse for fun target["AmbientColor"] = target._scriptingTutorial1GetRandomColorAsString() target["DiffuseColor"] = target._scriptingTutorial1GetRandomColorAsString() . default // Allow other events to fall through, doing a println println(args.MouseEventType) . . . .
You may notice that the only two events you get in Hero's Journey right now are Enter and Leave, that is because the other events are overridden to support a legacy implementation until such a time as we refactor that particular aspect of Hero's Journey.
Get a Random Positional Offset
This method produces a random offset to apply to the current position of our object by producing a value of 1 to -1 units of offset.
method _scriptingTutorial1GetRandomPositionalOffset() as Vector3 // Select a random positional offset along the x and z axis // offset as Vector3 offset.x = RandomFloat() * 2.0 - 1.0 offset.y = 0.0 offset.z = RandomFloat() * 2.0 - 1.0 return offset .
Reposition the Object
Using the Random Positional Offset method we created for our class, this method repositions the object by setting its position to its current position plus the offset.
If you look closely at the Properties Panel, you should notice 'Position is one of the properties listed there. HSL may manipulate any of the properties you see using a special syntax of aNode["PropertyName"] = value.
method _scriptingTutorial1SimpleReposition() // Simple routine to set the position of this node to a new position based on an random offset // // Get current Position, the external function GetNodePosition is faster than // accessing the value as a Property target["Position"] Pos as Vector3 GetNodePosition( me, Pos ) // Adjust the desired position to the current position plus a random offset Pos = Pos + me._scriptingTutorial1GetRandomPositionalOffset() SetNodePosition( me, Pos ) .
Random colors for the object's ambient and diffuse colors =
Change the Properties for Ambient and Diffuse lighting for an object to two random colors. The required format for various properties can be found here.
method _scriptingTutorial1GetRandomColorAsString( ) as String // Choose a random color and set the ambient or diffuse color for this node // var r = randomFloat() var g = randomFloat() var b = randomFloat() var a = randomFloat() // Clamp the alpha to no less than 50% if a < 0.5 a = 1.0 - a . // Properties have special string formating that is parsed // For plain old RGBA color it is #r,g,b,a where rgba are floats color as String = "#" + r + "," + g + "," + b + "," + a return color .
Look at the sample code
- With the Script Editor, open the _scriptingTutorial1ClassMethods script (if you do not see this script, make sure the "Show Engine" checkbox is checked)
- Compile the script with the #define simple commented out
- Place the _scriptingTutorial1 class on a new square/sphere, using the CLI command /mnac, make sure the node is marked as MouseTargetable.
- Observe how its behavior differs from the way your class was implemented.
Color change over time
Using a TimeLine
The easiest way to get color properties, is to set a node's properties with the Properties Panel, and then copy/paste the resulting string property into your script:
- Select your node, and view its properties in the properties panel
- Click on Diffuse or Ambient lighting.
- Use the dropdown box to switch to the TimeLine GUI.
- Set up a timeline for the colors you want
- Close the gui.
- Your timeline should now be converted to a string in the Properties Panel
- Copy the string into your script, to set the AmbientColor and DiffuseColor properties.
Using a Timer
Instead of changing the color each time the mouse enters or with a timeline, add a timer field to your class and change the color at some time interval. You will need to start the timer the first time the mouse enters...
Where should I go from here?
A more advanced tutorial that makes a good followup is the Replication Tutorial which addresses a number of more advanced concepts with a lot of detail.
- Input System - Details the workings of the Input System, Keybindings and Command Layers
- Asset Library - Powerful tool for the organization, specification and grouping of assets.