playMaker

Author Topic: GetNextRaycastAllHit  (Read 11169 times)

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
GetNextRaycastAllHit
« on: March 22, 2012, 04:33:54 PM »
Hi,

 Following a request: http://hutonggames.com/playmakerforum/index.php?topic=1203.0
 
Please find an action that will loop through all the hits from a raycast.

Instructions: use the GetRayCastHitInfo within the loop so that you can retrieve the current hit being iterated.


Code: [Select]
// (c) Copyright HutongGames, LLC 2010-2012. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.GameObject)]
[Tooltip("Performs a rayCast hit and Each time this action is called it gets the next hit. This lets you quickly loop through all the hits to perform actions on them.")]
public class GetNextRayCastAllHit : FsmStateAction
{

[ActionSection("Raycast Settings")]

[Tooltip("Start ray at game object position. \nOr use From Position parameter.")]
public FsmOwnerDefault fromGameObject;

[Tooltip("Start ray at a vector3 world position. \nOr use Game Object parameter.")]
public FsmVector3 fromPosition;

[Tooltip("A vector3 direction vector")]
public FsmVector3 direction;

[Tooltip("Cast the ray in world or local space. Note if no Game Object is specfied, the direction is in world space.")]
public Space space;

[Tooltip("The length of the ray. Set to -1 for infinity.")]
public FsmFloat distance;

[UIHint(UIHint.Layer)]
[Tooltip("Pick only from these layers.")]
public FsmInt[] layerMask;

[Tooltip("Invert the mask, so you pick from all layers except those defined above.")]
public FsmBool invertMask;

[ActionSection("RayCast Debug")]

[Tooltip("The color to use for the debug line.")]
public FsmColor debugColor;

[Tooltip("Draw a debug line. Note: Check Gizmos in the Game View to see it in game.")]
public FsmBool debug;

[ActionSection("Hit")]


[RequiredField]
[Tooltip("Event to send to get the next child.")]
public FsmEvent loopEvent;

[Tooltip("Event to send if there is no hit at all")]
public FsmEvent noHitEvent;

[RequiredField]
[Tooltip("Event to send when there are no more hits to loop.")]
public FsmEvent finishedEvent;

public override void Reset()
{

fromGameObject = null;
fromPosition = new FsmVector3 { UseVariable = true };
direction = new FsmVector3 { UseVariable = true };
space = Space.Self;
distance = 100;


layerMask = new FsmInt[0];
invertMask = false;
debugColor = Color.yellow;
debug = false;



loopEvent = null;
finishedEvent = null;
noHitEvent = null;
}

// cache the hits
private RaycastHit[] hits;

// increment a hit index as we loop through the hits
private int nextHitIndex;


public override void OnEnter()
{

if (nextHitIndex==0)
{
DoRaycastAll();
}

if (hits.Length==0)
{
nextHitIndex = 0;
Fsm.Event(noHitEvent);
Fsm.Event(finishedEvent);
Finish();
return;
}

if (nextHitIndex>=hits.Length)
{
nextHitIndex = 0;
Fsm.Event(finishedEvent);
Finish();
return;
}

Debug.Log("getting index"+nextHitIndex );
Fsm.RaycastHitInfo = hits[nextHitIndex];

nextHitIndex++;

Fsm.Event(loopEvent);

Finish();
}


void DoRaycastAll()
{
Debug.Log("DoRaycastAll");

if (distance.Value == 0)
{
return;
}

var go = Fsm.GetOwnerDefaultTarget(fromGameObject);

var originPos = go != null ? go.transform.position : fromPosition.Value;

var rayLength = Mathf.Infinity;
if (distance.Value > 0 )
{
rayLength = distance.Value;
}

var dirVector = direction.Value;
if(go != null && space == Space.Self)
{
dirVector = go.transform.TransformDirection(direction.Value);
}

hits = Physics.RaycastAll(originPos, dirVector, rayLength, ActionHelpers.LayerArrayToLayerMask(layerMask, invertMask.Value));

if (debug.Value)
{
var debugRayLength = Mathf.Min(rayLength, 1000);
Debug.DrawLine(originPos, originPos + dirVector * debugRayLength, debugColor.Value);
}

if (hits.Length==0)
{
Fsm.Event(noHitEvent);
}

}


}
}


If you have any questions, don't hesitate. It's quite an "advanced" action.

Bye,

 Jean
« Last Edit: March 22, 2012, 04:39:30 PM by jeanfabre »

amaranth

  • Full Member
  • ***
  • Posts: 172
Re: GetNextRaycastAllHit
« Reply #1 on: January 16, 2013, 07:05:58 PM »
Thank you, Jean. It turns out I needed this today. Saved me lots of work.

amaranth

  • Full Member
  • ***
  • Posts: 172
Re: GetNextRaycastAllHit
« Reply #2 on: January 23, 2013, 07:53:59 PM »
I wanted to add one small thing for anyone else who uses this. If you exit out of the loop early, the loop counter inside the action doesn't reset (by design), and if you use the loop again, the order of your objects will still have the counter from the first loop. To fix, I added a FsmBool called resetIndex to my code. I call GetNextRaycastAllHit in a separate state to reset this number before I call GetNextRaycastAllHit to get the raycast. Love this action, btw!

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: GetNextRaycastAllHit
« Reply #3 on: January 24, 2013, 08:37:53 AM »
Hi,

 It does reset if you come back to the action from a non direct path, could you explain the use case? I am interested to understand why you come back state if you want to quit earlier on.

bye,

 Jean

amaranth

  • Full Member
  • ***
  • Posts: 172
Re: GetNextRaycastAllHit
« Reply #4 on: January 24, 2013, 02:13:43 PM »
My workaround above didn't work, so I found another solution.

Here is what I'm doing:
The player can pick up creatures. If there are three creatures in one spot, only one creature needs to be picked up. It is possible that there may be plants in the way. Plants should be ignored.

Here is what happened:
(1) I used fingergestures as my start/idle state.
(2) When a click is found, the FSM jumps to the next state, which contains GetNextRaycastAllHit.
(3) GetNextRaycastAllHit gets a list of all game objects under the mouse (creatures, plants).
(4) A loop checks to see if the first object is a creature or plant. If it's a creature, the loop should break. If it's a plant, the loop should continue until a creature is found or all objects have been checked.
(5) If the first object is a creature, the loop breaks, and I can move the creature around.
(6) The FSM goes back to the start/idle state (directly linked to state that contains GetNextRaycastAllHit).
(7) Click on another creature. Data for the previous creature still in memory.

Hopefully this helps? It sounds like it's important not to have a direct link between the idle and raycast states?

e1ite

  • Playmaker Newbie
  • *
  • Posts: 4
Re: GetNextRaycastAllHit
« Reply #5 on: January 28, 2013, 11:31:23 AM »
@amaranth any way you can show some pictures of your FSM setup? I believe this is exactly what i was looking for, i'm also using fingergestures!

Thanks a million!
Jason

amaranth

  • Full Member
  • ***
  • Posts: 172
Re: GetNextRaycastAllHit
« Reply #6 on: February 12, 2013, 10:06:11 PM »
Sorry, this is a little late. I've attached my FSM setup.

e1ite

  • Playmaker Newbie
  • *
  • Posts: 4
Re: GetNextRaycastAllHit
« Reply #7 on: February 13, 2013, 10:15:58 PM »
Thanks!

choi

  • Playmaker Newbie
  • *
  • Posts: 40
Re: GetNextRaycastAllHit
« Reply #8 on: April 01, 2013, 09:27:42 PM »
Thank you.
recognizable painting terrain textures?

then00b

  • Playmaker Newbie
  • *
  • Posts: 8
    • @then00b
Re: GetNextRaycastAllHit
« Reply #9 on: February 23, 2014, 10:20:31 PM »
This action definitely prefers you don't leave the loop until it is finished if you intend to loop again after some action. Case in point, if you:

1. Iterate over hit objects
2. As soon as an object with matching tag is hit, move to some other event

The loop index will not reset to 0 and the next time you iterate through the hit objects, it will begin at the value you left it at.

To remedy this, try to always let it finish its loop THEN take an action. In the above example, you would set a boolean variable to true if it collides with a desired tag then continue through the loop, taking action only after the 'finish' event. Hopefully this makes sense.
-Tony

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: GetNextRaycastAllHit
« Reply #10 on: February 24, 2014, 08:22:47 AM »
Hi,

 How come you can have many items on the same screen pixel? Is this for a 2d UI interface of in 3d?

bye,

 Jean

then00b

  • Playmaker Newbie
  • *
  • Posts: 8
    • @then00b
Re: GetNextRaycastAllHit
« Reply #11 on: February 24, 2014, 09:03:20 PM »
Hi Jean,

It is for a 2D game whose sprite-based units are utilizing 3D colliders. I'm using it to draw a line from an object on a board to its parent unit to check if it passes through any 'enemy' units. In my previous example, if I cut out of the loop early as soon as it hit an enemy unit then came back to the action the next time I needed to check, the index of the array would not be reset to 0. Thus, I found it necessary to always iterate through ALL of the objects THEN act on the hit enemy unit.

I hope this makes sense. The action is performing the way I need it to just not quite in the manner I originally expected. Still it is quite helpful so thank you very much!

-Tony
-Tony

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: GetNextRaycastAllHit
« Reply #12 on: February 25, 2014, 02:46:25 AM »
Hi,

 I see, tricky yes.

bye,

Jean

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 15500
  • Official Playmaker Support
Re: GetNextRaycastAllHit
« Reply #13 on: May 06, 2014, 07:11:47 AM »
Hi,

 I have address this loop exit issue on the latest iteration that is now hosted on github ( and soon available via the ecosystem)

 Don't forget to tune to the new rss feed for all PlayMaker news, typically I did a two feed on that topic, one for the custom action itself and one for the micro sample demonstrating it use.

http://feeds.feedburner.com/PlaymakerEcosystem

Bye,

 Jean


Khoa1994

  • Playmaker Newbie
  • *
  • Posts: 33
Re: GetNextRaycastAllHit
« Reply #14 on: July 20, 2014, 02:47:03 AM »
Can you write the action "GetNextRaycastAllHitFromScreen"? I use the action "RaycastFromScreen" for my game. Now, I want to use the "GetNextRaycastAllHit" the same way I use "RaycastFromScreen".