Unity How to make a gameobject going through waypoints endlessly? - c#

I have this code:
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
public class AutoPilot : MonoBehaviour {
public Transform[] points;
private int destPoint = 0;
private NavMeshAgent agent;
void Start () {
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
GotoNextPoint();
}
void GotoNextPoint() {
// Returns if no points have been set up
if (points.Length == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].transform.position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Length;
}
void Update () {
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 2f)
GotoNextPoint();
}
}
Where I go through my waypoints and the agen follows them, but when I am at the last waypoint, how to make it restart itslef? to keep going?
Is it possible to reset somehow or what is the best way?

I fixed it by adding this:
if (destPoint == points.Length) {
destPoint = 0;
}

Related

Unity - deleting a specific gameobject when mouse/pointer is over it

I am creating a virtual reality game where when you double click on an object it deletes it. However multiple objects are duplicates of eachother so when i attach my double click script to them it will delete all the objects upon double click. I want it to just delete the one the mouse is on. I will attach my script below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class doubleClick : MonoBehaviour
{
private float firstClickTime, timebetweenClicks;
private bool coroutineAllowed;
private int clickCounter;
public GameObject toDelete;
// Start is called before the first frame update
void Start()
{
firstClickTime = 0f;
timebetweenClicks = 0.2f;
clickCounter = 0;
coroutineAllowed = true;
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonUp(0))
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
private IEnumerator DoubleClickDetection()
{
coroutineAllowed = false;
while (Time.time < firstClickTime + timebetweenClicks)
{
if (clickCounter == 2)
{
//Destroy(toDelete);
break;
}
yield return new WaitForEndOfFrame();
}
clickCounter = 0;
firstClickTime = 0f;
coroutineAllowed = true;
}
}
I can't see anything that jumps out in your code. Padia's answer is on the right track, though. Mouse input handling when you want to interact with objects is best done from within that object/prefab. That way, you're guaranteed that only that object is interacted with. If not, then there's likely something else going on with your code.
Unfortunately, it's a little late for me to work out a complete solution. But here's a possible solution.
Within the prefab's code, insert an OnMouseDown function. In that function, check if the double-click timer's been started. If not, set a flag to tell the prefab that the double-click timer needs to start (call it WaitingForTimerStart). You could alternatively use an enum to hold the timer state. In any event, don't start the timer yet.
Within the prefab's code, insert an OnMouseUp function. That should check if the timer start flag's (WaitingForTimerStart) been set. If it has, set another timer flag to tell it to run the timer (RunTimer).
In the prefab's Update function, run the timer. I'm sure you know what to do here.
In the prefab's OnMouseDown function (the one you added earlier), check if the timer's running. If it is, then you know a double-click's been performed. Destroy the object.
This way, there's no need to check names, use raycasting or coroutines. Each object will have its own timer and be fully independent of each other.
One main issue with your code is: You are starting the Coroutine everyframe!
You have to wrap your code block in { } after the if, otherwise the condition only applies for the one line after it!
Instead of
if (Input.GetMouseButtonUp(0))
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
it should probably rather be
if (Input.GetMouseButtonUp(0))
{
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
Then
I want it to just delete the one the mouse is on.
Well currently you are using Input.GetMouseButtonUp which is true global in the entire app, not only the object the mouse is currently over.
You can rather use e.g. OnMouseDown which is called when the mouse goes down over a collider or UI element.
private void OnMouseDown ()
{
clickCounter += 1;
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
Try this code:
private float firstClickTime, timebetweenClicks;
private bool coroutineAllowed;
private int clickCounter;
RaycastHit hit;
// Start is called before the first frame update
void Start()
{
firstClickTime = 0f;
timebetweenClicks = 0.2f;
clickCounter = 0;
coroutineAllowed = true;
}
private void OnMouseUp()
{
clickCounter += 1;
}
// Update is called once per frame
void Update()
{
if (clickCounter == 1 && coroutineAllowed)
{
firstClickTime = Time.time;
StartCoroutine(DoubleClickDetection());
}
}
private IEnumerator DoubleClickDetection()
{
coroutineAllowed = false;
while (Time.time < firstClickTime + timebetweenClicks)
{
if (clickCounter == 2)
{
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
if (hit.collider.transform == this.transform)
{
Destroy(this.gameObject);
}
}
break;
}
yield return new WaitForEndOfFrame();
}
clickCounter = 0;
firstClickTime = 0f;
coroutineAllowed = true;
}
This code use OnMouseDown() Unity function to detect when the game object is clicked by the mouse, and add 1 to the clickCounter variable.
Also you can use OnMouseUp() function if you prefer to detect the click when the mouse is up.

How can I make a smooth transition between two animations?

First animation when starting the game the character is examine or typing and then after 10 seconds I want the character to start walking and just before reaching the destination to change to idle.
The order should be : Typing , Walking , Idle
With this script the character is typing waiting 10 seconds rotating looking the target and then move to the target without the two animations Walking and Idle.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentControl : MonoBehaviour
{
public List<Transform> points;
public bool waitTimeToMove = false;
//notice WaitTime is a float now
public float WaitTime = 10f;
public bool randomWaitTime;
float waitMinTime = 1f;
float waitMaxTime = 10f;
public bool loop = false;
private int destPoint = 0;
private NavMeshAgent agent;
private Transform originalPos;
//two vars for handling timer
private float timer = 0;
private float originSpeed;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
agent.autoBraking = false;
originSpeed = agent.speed;
if (randomWaitTime == true)
{
WaitTime = Random.Range(waitMinTime, waitMaxTime);
}
//transforms dont exist without A GameObject and a GameObject doesn't exist without a transform
//create a new GameObject to hold our position
GameObject originalPositionObject = new GameObject();
originalPositionObject.name = "WP";
originalPositionObject.tag = "Waypoint";
originalPositionObject.transform.parent = GameObject.Find("Waypoints").transform;
//set the new gameobjects position equal to where the transform is right now
originalPositionObject.transform.position = transform.position;
//add this to the points list instead
points.Add(originalPositionObject.transform);
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Count == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Count;
}
void Update()
{
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 1f)
{
//if wait to move is true
if (waitTimeToMove)
{
//if timer is less than 10
if (timer < WaitTime)
{
//add Time.deltaTime each time we hit this point
timer += Time.deltaTime;
}
//no longer waiting because timer is greater than 10
else
{
waitTimeToMove = false;
}
}
//if we hit here waitToMove is false, so go ahead as usual
else
{
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
}
if (loop == true || destPoint != points.Count - 1)
{
agent.speed = originSpeed;
// Not working if setting back to loop = true in the inspector
// After it was not loop and agent in last waypoint
// When setting to loop = true it's not continue to check why
// Should continue the loop if loop true !
// Loop = true is not working when game is running only on Start
GotoNextPoint();
}
}
}
}
}
In the editor I have an animator controller for the character with 3 states : The touc/examine , walk , idle
The first animation is working fine but it's not changing to walk after 10 seconds and not changing to walk at all. It keep looping the first animation all the time. and the first animation length is more then 0 seconds.
between the first animation state and the second walk state there is a transition and exit time is disabled with one condition Walk True and a parameter I added of type bool name Walk.
between the second state walk and the last state idle also there is a transition and exit time disabled too with condition Walk True.
Screenshot of the first transition :
The main idea is to change transitions smooth between the 3 states and then stop when the last state played without looping. Each state should play once.
What I did so far :
In both transitions disabled Has Exit Time (enabled false)
Then changed the script added a line to start the Walk animation state :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentControl : MonoBehaviour
{
public List<Transform> points;
public bool waitTimeToMove = false;
//notice WaitTime is a float now
public float WaitTime = 10f;
public bool randomWaitTime;
float waitMinTime = 1f;
float waitMaxTime = 10f;
public bool loop = false;
public Animator anim;
private int destPoint = 0;
private NavMeshAgent agent;
private Transform originalPos;
//two vars for handling timer
private float timer = 0;
private float originSpeed;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
agent.autoBraking = false;
originSpeed = agent.speed;
if (randomWaitTime == true)
{
WaitTime = Random.Range(waitMinTime, waitMaxTime);
}
//transforms dont exist without A GameObject and a GameObject doesn't exist without a transform
//create a new GameObject to hold our position
GameObject originalPositionObject = new GameObject();
originalPositionObject.name = "WP";
originalPositionObject.tag = "Waypoint";
originalPositionObject.transform.parent = GameObject.Find("Waypoints").transform;
//set the new gameobjects position equal to where the transform is right now
originalPositionObject.transform.position = transform.position;
//add this to the points list instead
points.Add(originalPositionObject.transform);
anim = GetComponent<Animator>();
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Count == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Count;
}
void Update()
{
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 1f)
{
//if wait to move is true
if (waitTimeToMove)
{
//if timer is less than 10
if (timer < WaitTime)
{
//add Time.deltaTime each time we hit this point
timer += Time.deltaTime;
}
//no longer waiting because timer is greater than 10
else
{
waitTimeToMove = false;
anim.SetBool("Walk", true);
}
}
//if we hit here waitToMove is false, so go ahead as usual
else
{
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
}
if (loop == true || destPoint != points.Count - 1)
{
agent.speed = originSpeed;
// Not working if setting back to loop = true in the inspector
// After it was not loop and agent in last waypoint
// When setting to loop = true it's not continue to check why
// Should continue the loop if loop true !
// Loop = true is not working when game is running only on Start
GotoNextPoint();
}
}
}
}
}
After 10 seconds :
anim.SetBool("Walk", true);
but now I want that a bit before or when the agent get to the target destination to change it to the Idle state animation :
anim.SetBool("Idle", true);
but I'm not sure where to put it in the script. I tried in this place :
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
anim.SetBool("Idle", true);
}
but it didn't work fine the agent kept walking after the target destination then moved back to the target destination then changed to idle a mess.

Not cycling through an array as expected

I'm trying to get the player to continue on after he jumps (see code), but what he does is return back to the jumpPosition transform without continuing on to the next point.
Here is what it is doing in Unity:
...
Here is my code for playerMovment:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MovementController : MonoBehaviour
{
// public GameObject playerToMove; // not sure why I need this
public float moveSpeed; // move speed of the player going from player postion to current point, possible to use somewhere else
private Transform currentPoint; // used to determine where the next point the player has to move by cycling through 'points' array
public Transform jumpPoint; // used as a location trigger to tell the player when to jump -- will be attempting to make into an array
public Transform crouchPoint; // used as a location trigger to tell the player when to crounch -- will be attempting to make into an array
public Transform[] points; // an array of location for the 'currentPoint' to cycle through
public float maxPause = 100; // used to determine the length of time between when the player arrives at the 'currentPoint' and when to leave said point; default = 100
public float reducedPause = 2; // used to set 'maxPause' to a smaller number so that player won't keep jumping/crouching
public TestCharacterController2D controller; // acceses the TestCharacterController2D script (I didnt write this script but plan to modiify) used for basic move, jump, and crouch funtions
public Animator animator; // my attempt to find the player's animator
public bool isRight; // used to to determine which way the character is facing -- I think this can be accesed through the 'controller' variable (TestCharacterController2D script)
private bool jump; // to tell the 'controller' when to jump
private bool crouch; // to tell the 'controller' when to crouch
private bool pause = false; // used to determine when the player arrives at the 'currentPoint' and the 'maxPause' countdown begins
public int pointsSelection; // used to cycle the 'points' array when maxPause cycle is over and player is at current point
// public float jumpHeight = 100f; // not sure why used
void Start() // looking into 'onAwake' maybe? (or others)
{
currentPoint = points[pointsSelection]; // sets currentPoint to default location ('pointSelection' is 'publc' so can be modified in Unity
isRight = true; // player starts facing right -- as per character animations
}
void Update() // not sure if should have more in 'FixedUpdate' or others (maybe?)
{
jump = false;
if (Vector2.Distance(transform.position, currentPoint.position) < 0.05f)
// checks to see if player is at 'currentPoint'
{
pause = true; // starts the pause sequenece
Debug.Log("Pause = " + pause);
if (pause) // when the movement is pause do the the following
{
moveSpeed = 0;
animator.SetFloat("Speed", 0); // player stops moving -- works!
if (maxPause <= 100) // checks to see if still paused
{
Debug.Log("this is maxPause: " + maxPause);
if (maxPause < 0) // found 'maxPause' was going to far below zero
maxPause = 0;
maxPause--; // reduce pause amount (working way out of loop)
}
if (maxPause == 0) // when 'maxPause' timer has finished
{
pointsSelection++; // move to next point
maxPause = 100; // reset 'maxPause' timer
pause = false; // resume 'transform.position == currentPoint.position' process
}
}
if (pointsSelection == points.Length) // makes sure 'pointsSelection' doesn't go out of bounds
{
Debug.Log("at end of array");
pointsSelection = 0; // start the player's movement process over again
}
}
else // not sure if requried
{
Debug.Log("pause = false");
Debug.Log("this is the moveSpeed " + moveSpeed);
Debug.Log("pointsSelection: " + pointsSelection);
}
if (Vector2.Distance(transform.position, jumpPoint.position) < 0.05f && jumpPoint == currentPoint) // conditions for the jump action (automatic) -- I fell the whole thing needs to be more elaborate ** WORK IN PROGRESS **
{
jump = true;
}
else
jump = false;
currentPoint = points[pointsSelection]; // moved to line 130 -- not sure if better here
}
void FixedUpdate()
{
if (isRight && transform.position.x > currentPoint.position.x) // flipping the character -- I'm pretty sure I can use TestCharacterController2D to do this for me, this is comparing the player's 'transform'
{
moveSpeed = -0.25f; // tells controller to head in the left direction
isRight = false; // no longer facing right
}
if (!isRight && transform.position.x < currentPoint.position.x) // reverse of above
{
moveSpeed = 0.25f; // tells controller to head in the right direction
isRight = true; // no longer facing left
}
if (moveSpeed > 0 || moveSpeed < 0)
animator.SetFloat("Speed", 1); // player starts PlayerRun animation -- works!
// Move our character
controller.Move(moveSpeed, crouch, jump); // draws from the TestCharacterController2D script
}
public void OnLanding()
{
animator.SetBool("Jumped", false);
}
}

How do I carry over data between scenes in Unity?

For a game I am developing I have a part when if the user gets too close to an enemy it switches scene to a battle scene. However I have no clue how to load that enemy into the battle screen (given that a user can battle many different enemies). Below is my current cod for the enemy. I was wondering if I could carry over it's name into the next scene or something. I just want my enemy to go from one screen to another when the scene is changed. Code would be appreciated thankyou
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FolllowAndLoad : MonoBehaviour
{
public Transform target;
public Animator anim;
public Rigidbody2D myRigidBody;
public string levelToLoad;
private static string keyname; // value I want to carry over
public float MoveSpeed;
private bool checkTrigger;
public Rigidbody2D targetRigidBody;
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();//getting the position of our player
anim = GetComponent<Animator>();
myRigidBody = GetComponent<Rigidbody2D>(); //getting my components
targetRigidBody = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();
}
void Update()
{
float distance = Vector2.Distance(target.position, myRigidBody.transform.position); //getting the distance between our player and our enemy
if (distance < 5)
{
transform.position = Vector2.MoveTowards(transform.position, target.position, MoveSpeed * Time.deltaTime); //moving our enemy towards our player
anim.SetBool("checkTrigger", true);
anim.SetFloat("MoveX", moveXvalue()); //updating the animations for our enemy
anim.SetFloat("MoveY", moveYvalue());
}
else if (distance > 5) //if out of range stop walking
{
anim.SetBool("checkTrigger", false);
}
}
int moveXvalue()
{
int value;
if (myRigidBody.transform.position.x < target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x)) //these are saying if the enemy is closer in x than in y use x animations and vice versa
value = 1;
else if (myRigidBody.transform.position.x > target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
value = -1;
else
value = 0;
return value;
}
int moveYvalue()
{
int value;
if (myRigidBody.transform.position.y < target.transform.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
value = 1;
else if (myRigidBody.transform.position.x > target.transform.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
value = -1;
else
value = 0;
return value;
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
Debug.Log(gameObject.name);
anim.SetBool("checkInContact", true);
Application.LoadLevel (levelToLoad); //loading our level
}
}
}
There are a lot of ways to do this, but the simplest way to get something working quickly just until you get more familiar with Unity it to use a simple static class in your project that you can access from any script in any scene.
So if you were to make a new script in your project right now called SharedResources.cs and then pasted this into the script and saved it....
public static class SharedResources
{
public const int kSceneIs_TitleScene = 0;
public const int kSceneIs_ActualGameScene = 1;
public const int kSceneIs_HighScoreScene = 2;
public static int highScore = 0;
public static int enemyID = 0;
public static void sampleFunction()
{
//this is a sample method you can call from any other script
}
}
You could now be in a script in one scene and do this
SharedResources.highScore=SharedResources.highScore+20;
SharedResources.enemyID=5;
You could then open up a new scene and a script in that scene could access the high score
Debug.Log(SharedResources.highScore)
Debug.Log(SharedResources.enemyID)
You can also access constant and run subroutines that are in the static class as shown above.
The correct way to do this is up for debate and really depends on what your ultimate goal is. I will reference another link to a post that goes into more detail....
https://gamedev.stackexchange.com/questions/110958/unity-5-what-is-the-proper-way-to-handle-data-between-scenes
Ideally, you should read and understand the difference between using a simple static class versus one that derives from MonoBehavior, and also the different between a static class and a Singleton, which in many ways is much more powerful (but can also cause issues if you don't code it correctly)
Last but not least, don't forget you can also use the built in PlayerPrefs function in Unity to store scores and other settings that need to carry over between launches of the game....
https://answers.unity.com/questions/1325056/how-to-use-playerprefs-2.html

How do I get a component from a ever changing List of objects?

while working in a 3d endless runner game in unity I came across this issue. I have a List of platforms(segments/roads) that lay in front of the player while the player runs in z direction. I have downloaded a new asset package called Dreamteck splines. So each platform has a spline component attached to it. Once a platform is laid the player grabs the spline and runs according to the pattern of the spline.
Let's say that the player is on the first platform. When the player reaches the end of the first platform's spline, the OnEndReached() event handler is called, which basically says what you want to happen when the spline's endpoint is reached. So I want to know how to I get the next spline once the end is reached.
P = player
As seen in the image above this is what I am trying to accomplish. As a brief description of how platforms are laid is that once the player goes to the next road the one he just passed gets disabled so next time he can reuse the road in front of the player in random manner.
The code: track manager script.
public Segment[] tilePrefabs;
public static Segment newSegment;
public static List<Segment> m_Segments;
public static List<Segment> m_PastSegements;
private int m_SafeSegmentLeft;
private int m_PreSegments = -1;
private float startingSegmentDistance = 4f;
private int startingSafeSegments = 2;
private int amtSegmentsOnScreen = 10;
private float segmentRemovalDistace = -40f;
private float m_TotalWorldDistance;
private float m_CurrentSegmentDistance;
void Update ()
{
while (m_Segments.Count < amtSegmentsOnScreen)
{
SpawnNewSegment();
}
m_TotalWorldDistance += scaledSpeed;
m_CurrentSegmentDistance += scaledSpeed;
if (m_CurrentSegmentDistance > m_Segments[0].worldLength)
{
m_CurrentSegmentDistance -= m_Segments[0].worldLength;
m_PastSegements.Add(m_Segments[0]);
m_Segments.RemoveAt(0);
}
Vector3 currentPos;
Quaternion currentRot;
Transform playerTransform = playerMotor.transform;
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
bool needRecenter = currentPos.sqrMagnitude > floatingOriginThreshold;
if (needRecenter)
{
int count = m_Segments.Count;
for (int i = 0; i < count; i++)
{
m_Segments[i].transform.position -= currentPos;
}
count = m_PastSegements.Count;
for (int i = 0; i < count; i++)
{
m_PastSegements[i].transform.position -= currentPos;
}
m_Segments[0].GetPointAtInWorldUnit(m_CurrentSegmentDistance, out currentPos, out currentRot);
}
playerTransform.rotation = currentRot;
playerTransform.position = currentPos;
for (int i = 0; i < m_PastSegements.Count; i++)
{
if ((m_PastSegements[i].transform.position - currentPos).z < segmentRemovalDistace)
{
m_PastSegements[i].Cleanup();
m_PastSegements.RemoveAt(i);
i--;
}
}
}
public void SpawnNewSegment()
{
int useSegment = Random.Range(0, tilePrefabs.Length);
if (useSegment == m_PreSegments)
{
useSegment = (useSegment + 1) % tilePrefabs.Length;
}
Segment segmentToUse = tilePrefabs[useSegment];
newSegment = Instantiate(segmentToUse, Vector3.zero, Quaternion.identity);
Vector3 currentExitPoint;
Quaternion currentExitRotation;
if (m_Segments.Count > 0)
m_Segments[m_Segments.Count - 1].GetPointAt(1.0f, out currentExitPoint, out currentExitRotation);
else
{
currentExitPoint = transform.position;
currentExitRotation = transform.rotation;
}
newSegment.transform.rotation = currentExitRotation;
Vector3 entryPoint;
Quaternion entryRotation;
newSegment.GetPointAt(0.0f, out entryPoint, out entryRotation);
Vector3 pos = currentExitPoint + (newSegment.transform.position - entryPoint);
newSegment.transform.position = pos;
newSegment.manager = this;
newSegment.transform.localScale = new Vector3((Random.value > 0.5f ? -1 : 1), 1, 1);
newSegment.objectRoot.localScale = new Vector3(1.0f / newSegment.transform.localScale.x, 1, 1);
if (m_SafeSegmentLeft <= 0)
SpawnObstacle(newSegment);
else
m_SafeSegmentLeft -= 1;
m_Segments.Add(newSegment);
}
The player script
//Current tile segment;
private Segment currentSegment;
//Spline Follower
private SplineFollower follower
//For Dreamteck spline -->
private Segment nextSegment;
void Start()
{
playerCollider = GetComponent<CapsuleCollider>();
anim = GetComponent<Animator>();
follower = GetComponent<SplineFollower>();
moveLane = currentLane;
follower.onEndReached += Follower_onEndReached;
}
private void Follower_onEndReached()
{
currentSegment = nextSegment;
follower.computer = currentSegment.spline;
}
void OnTriggerEnter(Collider col)
{
nextSegment = col.GetComponentInParent<Segment>();
}
The segment script : Attached to each road/ platform
public SplineComputer spline;
public static Segment next;
SplinePoint[] points;
void Start()
{
spline = GetComponentInChildren<SplineComputer>();
spline.space = SplineComputer.Space.Local;
points = spline.GetPoints();
if (points.Length == 0)
return;
}
At the moment I use colliders, each road has a box collider component. Once the player reach end of the platform it does get the next spline component. It works but sometimes it fails to recognize the next spline and use the same spline which causes the player to run the same platform that he passed again and again.
So I'm out of ideas. So came here to find a solution or advice. Help would be appreciated.
In this case I would simply store my possible segments in a List then when I reached the end get the next segment and move the current 1st segment to the end or where ever you want to move it in the list.
That is
public Segment currentSegment;
public List<Segment> segments;
void OnEndReached()
{
//Put the completed segmetn back in the list ... probably at the end or randomized anywhere but at the start
segments.Insert(segments.Count-1, currentSegment);
//Now get the next segment
currentSegment = segments[0];
segments.RemoveAt(0);
}
In this model you have a simple List which represents the order your segments will appear in, you always set the current segment to the next in the list e.g. index 0 and you put them back in the list when done... putting them at the end or if you want to randomize order slotting them in anyware except index 0 e.g.
segments.Insert(UnityEngine.Random.Range(1, segments.Count), currentSegment);
Note that I removed the segment I am on from the list ... the list just represents the upcoming order which in runner games I find it handy to know that e.g. so I can reset things, change attributes of the segments based on performance, score, etc.
Using OnTriggerEnter, or OnTriggerEnter2D if you're dealing with 2D colliders, should do the job. But as you say you already are working with colliders, I assume this is what you have tried.
You could try:
OnTriggerStay
A Raycast down to the ground object. In this link is a 2D Example: https://kylewbanks.com/blog/unity-2d-checking-if-a-character-or-object-is-on-the-ground-using-raycasts
You can also raycast with 3D objects.
What you would be using it for in this case is basically to shoot a "laser" into the ground below your player and grab an object there based on which layers you tell it to hit. So if you have a layer called "Ground" which your platform is part of, then it can only return objects from that layer.
Just remember to raycast often so it's updated to reflect the game.

Categories