HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.

Author Topic: States System - movable objects  (Read 188 times)

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
States System - movable objects
« on: Aug 10, 17, 08:40:57 AM »

Moderate scripting

This will be 2 part. First part will add the classes, fields and scripts, then second part will create an object using the system. I'm going to add a little more detail here and explain a few things better.

In the DOM (Server side), create a class E_States

The name doesn't matter too much, but this is the name I used. The 2nd function is mostly from the wiki States System . We call it from client giving it the node it needs to make the object. Mostly what this function does is set up the GUI for the dev to use, it pre-sets the values so most of the work is done. The 1st function I made, using some code also from the wiki, but added a few checks. This function called from client, will check that the targeted object is already a State System Object, in order to change its state. Compile the server side script 1st, as the client side calls these functions.

click open script

Code: [Select]
// change the current state of this object
untrusted method ClickyChangeState(objectID as ID, startState as String, endState as String)

  object as NodeRef = objectID
  if object == None
    return
  .
  if not hasmethod(object, "_GetMyStateObject") // make sure it can change
    return
  .
  areaStates as NodeRef of Class _StateObjects = $STATES._GetStatesNodeByClass("_areaStates")
  state as NodeRef of Class _StateObject = object._GetMyStateObject()
  currentState as String = state._GetStateValue()

  if currentState == startState
    println("changing from "+startState+"  to  "+endState)
    state._SetStateByValue(endState)
  else
    println("changing from "+endState+"  to  "+startState)
    state._SetStateByValue(startState)
  .
.

// make an Object into a Stateful Object
untrusted method MakeStatefulObject(object as NodeRef)
  println("MakeStatefulObject "+object)

  // assume object is a noderef to an asset that is a object we wish to make stateful
  statefulObject as NodeRef = object
  areaStates as NodeRef of Class _StateObjects = $STATES._GetStatesNodeByClass( "_areaStates")
  areaStateName as String = "Obj_"+object
 
  // Create the AreaState node
  var state = areaSTates._CreateStateObject( areaStateName )
 
  // GLOM on the stateful object class or one of its children, this supports right-click menus for wiring
  //   finding the object's state and editing the state
  GLOMClass("_statefulObject", statefulObject )

  // Change base_hard_association to link it to the statefulObject, allowing simultaneous deletes
  state._SwapStateBaseHardAssociationToTarget( statefulObject )

// *** these preset the values in the States system right click menu ***
  var values = state._GetStateValidValuesRoot()
  // Set valid values to use for transitions
  values._CreateStateValidValueForValue( "OPENED" )
  values._CreateStateValidValueForValue( "CLOSED" )

  // default state, ie all doors start closed
  state._SetStateDefaultValueByValue( "CLOSED" )

  var actionLists = state._GetStateActionListsRoot()
  // Create the actionList 
  var openActionList = actionLists._GetStateActionListByName( "open" + areaStateName )
  var closeActionList = actionLists._GetStateActionListByName( "close" + areaStateName )
  var initOpenActionlist = actionLists._GetStateActionListByName( "initOpen" + areaStateName )
  var initClosedActionList = actionLists._GetStateActionListByName( "initClose" + areaStateName )

  var transitions = state._GetStateTransitionsRoot()
  // Add Transitions
  var initOpen = transitions._CreateStateTransitionByValues( "NOTSET", "OPENED", "initOpen" + areaStateName )
  var open = transitions._CreateStateTransitionByValues( "CLOSED", "OPENED", "open" + areaStateName )
  var close = transitions._CreateStateTransitionByValues( "OPENED", "CLOSED", "close" + areaStateName )
  var initClose = transitions._CreateStateTransitionByValues( "NOTSET", "CLOSED", "initClose" + areaStateName )

  switchID as NodeRef = statefulObject
  basetime as TimeInterval = 0:00:05.000

  // Add actions to actionlists
  var openTransform = $STATES._StatesObjectCreateStateActionTransform( "_areaStates", switchID, "0,0,0", "0,0,0", "0,0,0", basetime, CLIENT )
  var closeTransForm = $STATES._StatesObjectCreateStateActionTransform( "_areaStates", switchID, "0,0,0", "0,0,0", "0,0,0", basetime, CLIENT )
  closeActionList._AddStateActionToActionList( closeTransForm )
  initClosedActionList._AddStateActionToActionList( closeTransform )
  initOpenActionList._AddStateActionToActionList( openTransform )
  openActionList._AddStateActionToActionList( openTransform )
 
  // Initialize to the default value
  state._SetStateByValue( "CLOSED" )
.

_______________________________________________________________
In the DOM, look for a (client side) class - HBNode

We are not going to change it, but going add a script for it. When objects are made, NPCs, Player chars, clicky type objects, they use HBnode for a base class.

The 1st function InputMouseEvent() checks for any mouse movement and responds based on what the user did, with hovering over or clicking on the object. There is a check for other type of objects, to see if they need a different response than what we have here. Since we don't want to make player chars into clicky objs, we skip if they are the object targeted. IsHeroBlade() is a check to see if the user is a dev in the editor, if not skip this part so the player won't use it. We don't want to make NPCs into stateful objects, so we skip them too.

When the mouse hovers over the object, we want to know which so we make the object glow with a green pulse, and when we are not over that object we reset it to normal.  On the click, since we don't want this to happen all the time, we check for 2 key presses and the mouse click in order to make our Target object into a Stateful Object which uses the States System. We also will have just a click on the object, to make it respond to the state system, which is usable by the players also. This will check the range (which can be changed from 3.5 meters) to the object, so that we don't effect objects from across the area. Meters are 1/10 the size of Units, which is what the base code is set up to use. Then we call server to make the object change state, or interact with the mouse click.

ShowClasses() is a function I made that will show which classes are on the node, since the control-T does not show classes on all objects, like the NPCs and player chars. GetRangeDistance() will check the range between the player and the targeted node, if the player is not in range it will toss a pup up message mid screen to the user. Next we have the color changes for the object used when mouse hovers over them.

click open script

Code: [Select]
// for the Highlighting of interactive objects in the world
// when the mouse hovers over it.
shared function InputMouseEvent(args references Class _MouseEvent)
  targ as NodeRef of Class HBNode = args.MouseTarget

//  if targ is kindof ClickyObject
//    ClickyObjectClassMethods:InputMouseEvent(args)
//    return
//  .
  if targ is kindof E_playerAccount
    return
  .

  // devs only
  if IsHeroBlade()
    if not (targ is kindof E_nonplayerCharacter)
      when args.MouseEventType
        is Enter
//          showClasses(targ)
          targ.greenPulses(targ)
        .
        is Leave
          targ.noPulses(targ)
        .
        is Click
          if args.leftButton and args._ctrlKey and args._altKey  // special check to avoid mishaps
            object as ID = args.MouseTarget
            println("making "+object+" into StatefulObject ")
            call server E_StatesClassMethods:MakeStatefulObject(object)
          .
        .
      .
    .
  .
  // all players
  when args.MouseEventType
    is Click
      if not (targ is kindof E_nonplayerCharacter)
        if args.leftButton
          var range = getRangeDistance(targ)
          if range < 3.5               // meters
            object as ID = args.MouseTarget
            println("toggle open/close for "+object)
            call server E_StatesClassMethods:ClickyChangeState(object, "CLOSED", "OPENED")
          .
        .
      .
    .
  .
.

function showClasses(target as NodeRef)
  println("** classes on target **")
  values as List of String = GetClassesOnNode(target)
  foreach val in values
    println("class on targ "+val)
  .
.

// check for object being out of clicking range
function getRangeDistance(target as NodeRef) as Float
  range as Float

  if not (target is kindof E_nonplayerCharacter)  // skip npcs
    var player = GetPlayerCharacterNode()
    ownerPos as Vector3
    getNodePosition(player, ownerPos)

    targetPos as Vector3
    getNodePosition(target, targetPos)

    range = VectorLength(targetPos - ownerPos) * 10 // in meters
    if range > 3.5
      msg as String = "You are too far away."
      color as Class rgba = MiscUtils:MakeRGBA( 1.0, 0.5, 0.5, 1.0 )
      // addtext: target, text, color, expand, size
      FloatingText:AddText(player, msg, color, false, false)
    .
  .
  return range
.

//=================================================================================================
// pulse green on mouse hover
method greenPulses(obj as NodeRef of Class HBNode)
  obj["DiffuseColor"] = "[0:#0.0,1.0,0.0,1.0:L][0.25:#0.0,4.0,0.0,1.0:L][0.5:#0.0,10.0,0.0,1.0:L][0.75:#0.0,4.0,0.0,1.0:L][1:#0.0,1.0,0.0,1.0:L]L;1.25 "
  obj["AmbientColor"] = "[0:#0.0,1.0,0.0,1.0:L][0.25:#0.0,4.0,0.0,1.0:L][0.5:#0.0,10.0,0.0,1.0:L][0.75:#0.0,4.0,0.0,1.0:L][1:#0.0,1.0,0.0,1.0:L]L;1.25"
.
// pulse white normally
method whitePulses(obj as NodeRef of Class HBNode)
  obj["DiffuseColor"] = "[0:#1.0,1.0,1.0,1.0:L][0.25:#4.0,4.0,4.0,1.0:L][0.5:#10.0,10.0,10.0,1.0:L][0.75:#4.0,4.0,4.0,1.0:L][1:#1.0,1.0,1.0,1.0:L]L;1.25 "
  obj["AmbientColor"] = "[0:#1.0,1.0,1.0,1.0:L][0.25:#4.0,4.0,4.0,1.0:L][0.5:#10.0,10.0,10.0,1.0:L][0.75:#4.0,4.0,4.0,1.0:L][1:#1.0,1.0,1.0,1.0:L]L;1.25"
.
// pulse red on reset
method redPulses(obj as NodeRef of Class HBNode)
  obj["DiffuseColor"] = "[0:#1.0,0.0,0.0,1.0:L][0.25:#4.0,0.0,0.0,1.0:L][0.5:#10.0,0.0,0.0,1.0:L][0.75:#4.0,0.0,0.0,1.0:L][1:#1.0,0.0,0.0,1.0:L]L;1.25"
  obj["AmbientColor"] = "[0:#1.0,0.0,0.0,1.0:L][0.25:#4.0,0.0,0.0,1.0:L][0.5:#10.0,0.0,0.0,1.0:L][0.75:#4.0,0.0,0.0,1.0:L][1:#1.0,0.0,0.0,1.0:L]L;1.25"
.
// reset to normal value
method noPulses(obj as NodeRef of Class HBNode)
  obj["DiffuseColor"] = "1,1,1,1"
  obj["AmbientColor"] = "1,1,1,1"
.


Next we will make an Object - simple door.
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
Re: States System - movable objects
« Reply #1 on: Aug 10, 17, 08:42:38 AM »

How to use the States System - making a simple door
from the wiki States System . (This might be a bit easy for most, but fun for those who are new.)

This next part we will make an object into clicky type object, showing how to basically use the States System. There are many more advanced features which can be made following the wiki. When 1st making a clicky object, make it in a non-Edit instance for practice. Once we understand a bit more, try it in the Edit instance. Though if a mistake is made, we can always delete the object.

On the top row of the Editor are pull down tabs, on the 2nd row are GUI pics to perform things the editor can do. On the 2nd from right, is a pic called whitebox (in tooltip). Click the pic to open window, select the cube in bottom right. It will place it right at camera, and set us in "select mode with transform" - 4th pic from left. Close the object window. Click the 3rd pic from left - "select mode". Both of these separate the character from the camera.

On the left, click the properties window to show its details. Look for MouseTargetable and change that to true. Change the Position to (0,-0.1,0)  to place object on ground, in middle of area. Set scale to 0.2, 1.5, 1  This will make it similar to a door shape. (Going to use this rather than the clean engine door, as its already a stateful object.)

Click "character mode" (1st pic), this will let us use the mouse to change its state. When we hover the mouse over the object, it should pulse green (using the HBnode script). Hold control and alt keys, then left click the object. It should tell us it made a stateful object and give its node ID. This ID should be the same in both the chat window (server) and the console window (client).

In "select mode" (pic 3), right click on the object. We should get the states system menu to pop up. Click Edit state to get our States System window. Most of the info will be filled in (using the server script function). Select the "action lists" tab, this will get us to where we can add/change things, to make the object do something when we click on it. Select "OPENOBJ_  ..." it should turn blue. We can double click  or push button "edit" to open it.

Another window will pop up with the default actions that this object was set up to do. Select "Transform" and open that window. The "target node" will be the same object ID we made into a stateful object. The "interval" is how long it takes to perform our movement, its written in hours:minutes:seconds fashion. The next 3 - position, rotation and scale are what the object can do using transform. We can make the door do different things by setting the right value. Don't set more than 1, as the combination can make the door seem wacky.

To make the door slide upward, we set position to (0,0.3,0)
To make it slide to the side, we set position to (0,0,0.2)
To make it rotate downward (like a drawbridge), we set rotation to (0,0,90)

Since the pivot point is set in the middle bottom of the cube, we can't make the door swing open from the side using rotation (0,90,0). (So when making objects, it's good to have the pivot point set in the right place for those objects that will move.) To save the changes click ok to save the Transform, and ok to save the action list, then ok to save the save the object States.

Open the State System menu, and select "CLOSEOBJ_ ...", and then on "transform" to get to the part when the door closes. Mostly we want the same values that are default, they tell the object how it should look when its back to its start value of "closed". Make sure the "interval" has the same time value we set in "open".

To test our object - door, select "character mode" (1st pic). Clicking on the object should now make the object respond to how we set it up. To delete an object, using "select mode" (3rd pic), target the object then push the "delete" key on keyboard.
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
Re: States System - movable objects
« Reply #2 on: Aug 10, 17, 08:51:51 AM »

I created this to show where we currently are at with our states system objects. Setting dynamic the object can be pushed by a character, change the physical density to a high value (like 99999999) to make the object not bounce all over.

There is an example area set up to show how objects can move around, in the Area Organizer list - FunWithPhysics.
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor

Prometheus2012

  • General Accounts
  • *
  • Posts: 627
    • View Profile
    • Exile-Online
Re: States System - movable objects
« Reply #3 on: Aug 10, 17, 10:02:06 AM »

Brilliant. The states system doesnt currently work out of the box even though it should. Some programming is required so this is really helpful.

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
Re: States System - movable objects
« Reply #4 on: Aug 11, 17, 08:43:09 PM »

How to use the States System - making a complex elevator

from the wiki States System . (This might be a bit easy for most, but fun for those who are new.)

This time we are making a more complex object, one that should carry the player character. We will be adding some of those advanced features to make the object perform. We will also add some scripts to help the character nodes work with the object.

Making the Object

Using the cube from the whitebox (in Door post), we are going to change a few things on the base model. In the properties window, set MouseTargetable to true, set PhysicsDensity to 99999999, set scale to 3,0.2,3, and set position to 0,-0.08,0 in middle of area.

Click "character mode", this will let us use the mouse to change its state. When we hover the mouse over the object, it should pulse green. Hold control and alt keys, then left click the object. It should tell us it made a stateful object and give its node ID. This ID should be the same in both the chat window (server) and the console window (client).

In "select mode", right click on the object. We should get the states system menu to pop up. Click Edit state to get our States System window. Most of the info will be filled in. Select the "action lists" tab, this will get us to where we can add/change things, to make the object do something when we click on it. Select "OPENOBJ_  ...", then select "Transform" and open that window. Set the Transform Interval to 0:00:05.000 and set the Positional Offset to (0,1,0) and click OK.

Next click Add, and select CallScript and click OK. This will add a new function to perform when the elevator goes up. For the script we add RideObjectClassMethods, for the data we add up__, for the target node make sure we have the platform selected and click the arrow on far right, for the Execute on select Client, then click OK. This will call our _OnDoStateAction() function to get and set our values. We use up__  as we set 4 spaces in string value to read.

We are going to add a 2nd function, click add and select CallScript and click OK. For the script we add RideObjectClassMethods, for the data we add none, for the target we click the arrow, for the Execute on select Client, and for delay we add 0:00:05.100, then click OK. Here we set "none" to let the controller script know the elevator is at the top, with the delay right after the elevator has reached the top using its interval time.

We are going to add a 3rd function to set the object to dynamic when it moves. Click add and select SetProperty, this will allow us to change values on the object. For target node click the arrow, set Property name to PhysicsType, set value to KINEMATIC, and click OK.

And a 4th function to reset the value back so that the object is not moved by player. Click add and select SetProperty. Click arrow for Target node, set property name to PhysicsType, set value to STATIC, set delay action to 0:00:05.100, and click OK. Again, the delay is for after the elevator reaches its destination.

We are going to add the same 4 functions to "CLOSEOBJ_ ...". Select the "transform" and open that window, set the Transform Interval to 0:00:05.000, and the rest will be 0s for reset values, and click OK.

Repeating the above, click Add, select CallScript, click OK. Script will be RideObjectClassMethods, data will be down, click arrow for target, Execute will be Client, and click OK.
Adding the 2nd function, click Add, select CallScript and click OK. Script = RideObjectClassMethods, data = none, click arrow for target, Execute = Client, then click OK.
Adding the 3rd function, click Add, select SetProperty and click OK. Click arrow for target, Property name = PhysicsType, value = KINEMATIC, and click OK.
Adding the 4th function, click Add, select SetProperty and click OK. Click arrow for target node, Property name = PhysicsType, value = STATIC, delay action = 0:00:05.100, and click OK. Click OK through all the window to save the data we added in.

Adding the scripts

The 1st function, gets the player and its position, then gets the object and its position. We use the ray cast to detect if the player is on the elevator. The ray cast is set up with some variables, and the player is excluded from the list of nodes to check. When the elevator is going up, we make sure the character is at the min height above the surface, so they don't fall thru the elevator.

The OnDoStateAction() is called by the base States System script when the CallScript option is added to the objects transition. Here we get the object and set up its variable, as the elevator is clicked. GetObject() and getDirection pull the values from a string that is created by the States System script sent thru the OnDoStateAction() function.

In the DOM (Client side), create a class RideObject

Create and add 3 fields:
rideDirection as string
rideNode as noderef
ridePosition as vector3

click open script, copy paste this in but don't compile yet, we need to add 1 more thing (under code).

Code: [Select]
// Get elevator and player, check if player is riding it using
// raycasting
public function DetectElevator(Accc as NodeRef of Class E_ACCController)
  var player = MiscUtils:getCharPointer()

  char as NodeRef = player._getACCController()._accControllerOwnerRef
  origin as Vector3
  GetNodePosition(char, origin)

  acct as NodeRef of Class E_playerAccount = GetAccountID()
  acct.ridePointer = GetAccountID()
  ride as NodeRef of Class RideObject = acct.ridePointer

  direction as Vector3 = (0,-1,0)  // detect below player
  intercept as Vector3
  distance as Float = 0.2          // detect upto 2 meters (0.2 units)
  meshName as String

  excludedNodes as List of NodeRef
  add back char to excludedNodes

  if ride.rideNode <> None
    GetNodePosition(ride.rideNode, ride.ridePosition)

    hit as NodeRef = Raycast3D(origin, direction, excludedNodes, distance, intercept, meshName)
    if intercept != (0,0,0)
      if ride.rideDirection = "up"
        check as Vector3
        check.y = ride.ridePosition.y + 0.05      // need 0.05 or player falls thru
          if origin.y < check.y                   // check player is above fall thru distance
            origin.y = check.y
          .
      .
      SetNodePosition(char, origin)
    .
  .

  // debug line to show direction of check
//  DrawDebugRay(origin, origin + (direction * 10), 0.05, "FF0000", "FF0000", 1.0, false)
//  if intercept != (0,0,0)
//    DrawDebugPoint(intercept, 0.1, "00FF00", 1.0, true)
//  .
.

// function called from main State System scripts - _StateActionCallScriptClassMethods()
// used with the CallScript option
shared function _OnDoStateAction( action as NodeRef of Class _StateAction )
  acct as NodeRef of Class E_playerAccount = GetAccountID()
  acct.ridePointer = GetAccountID()
  ride as NodeRef of Class RideObject = acct.ridePointer

  object as NodeRef = getObject(action)
  ObjLocate as Vector3
  GetNodePosition(object, ObjLocate)

  direction as String = getDirection(action)

  // save current object we are riding
  println("riding "+object+"  Dir "+direction)
  ride.rideDirection = direction
  ride.rideNode = object
  ride.ridePosition = ObjLocate
.

// grab object from string
function getObject(action as NodeRef of Class _StateAction) as NodeRef
  value as String = action._getStateActionAsString()
  start as Integer = FindString(value,"Target(") + 7  // 7 = size of "Target("

  node as String = SubString(value,start,19)          // 19 = size of node value
  object as NodeRef = node
  return object
.

// grab direction from string
function getDirection(action as NodeRef of Class _StateAction) as String
  value as String = action._getStateActionAsString()
  start as Integer = FindString(value,"data(") + 5  // 5 = size of "data("

  direction as String = SubString(value,start,4)    // 4 = size of node value
  direction = ReplaceString(direction,"_","")
  return direction
.


Now to keep players separate and not reading the same values, we add the class to the player account.

Create a field and add to E_PlayerAccount
ridePointer as   noderef of class RideObject


In the E_ACCControllerClassMethods script look for function - _HE_ACCC_SendMovementMessage()

in the function right above this line:
    if (needToSend)

add:
    RideObjectClassMethods:DetectElevator(me)

This will call our function during the movement of the elevator, to make sure the player stays on top of it.

I have had the elevator carry 1 player as it should, and when adding in a 2nd player, they can see the 1st riding the elevator. The 2nd seems to be able to ride sometimes, not always.
« Last Edit: Sep 06, 17, 07:10:37 PM by Thazager »
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor

nocake

  • General Accounts
  • *
  • Posts: 397
    • View Profile
    • BlightMMO
Re: States System - movable objects
« Reply #5 on: Aug 14, 17, 01:57:52 PM »

I have not read over this thread as I am in travel but IIRC there is an update to stateless script that was not applied to all the worlds. (if your world was created before a certain point or something).

Check this post:

https://community.heroengine.com/forums/index.php/topic,5701.msg32338.html#msg32338

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
Re: States System - movable objects
« Reply #6 on: Aug 14, 17, 07:14:33 PM »

It appears a bug may still present in the system,  as the dynamic objects don't always "work as intended". Yes, we do use that same code from the states system, and created the script to send an object to use the states system menu.

What would really help, is what variables of the physics object we would need to set, in order to make an object solid (other than static).
« Last Edit: Aug 14, 17, 07:16:37 PM by Thazager »
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor

nocake

  • General Accounts
  • *
  • Posts: 397
    • View Profile
    • BlightMMO
Re: States System - movable objects
« Reply #7 on: Aug 15, 17, 07:58:20 AM »

I wish i was home but i have a working door system in my world. Though i think i ditched the state system and judt made it a dynamic replicated object that updates accordingly. If you have access to my world you can copy it. I believe it also uses my extende dro system which i wrote a post about. If you need aces let me know.

Thazager

  • General Accounts
  • *
  • Posts: 1093
  • Never stop learning
    • View Profile
    • Heroes and Villains MMORPG
Re: States System - movable objects
« Reply #8 on: Aug 15, 17, 02:05:06 PM »

Thanks, we do have working doors in both worlds, and 1 world has clickable containers, while the other has clickable objects that flash. Neither has a great system for adding them in yet, but currently something workable.

Though its always interesting to see how others approached the situation.
Logged
Heroes and Villains Lead programmer/scripter
Exile Online Lead programmer/scriptor