Random Movement Script causes Unity to freeze - c#

I have a grid consisting of 16 tiles. Now basically I want the user to find a path to the final location by moving randomly on the choices he has within the grid.
As of now I managed to create the functions for moving a step up, down, left and right. The issue arises when I'm trying to code in random movement. Ideally this is setup in a way that he can't go off bounds from the grid.
This is what I got going:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
void Up()
{
//get the Input from Horizontal axis
float horizontalInput = Input.GetAxis("Horizontal");
//get the Input from Vertical axis
float verticalInput = Input.GetAxis("Vertical");
//update the position
transform.position = transform.position + new Vector3(0, 1.5f, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Down()
{
//get the Input from Horizontal axis
float horizontalInput = Input.GetAxis("Horizontal");
//get the Input from Vertical axis
float verticalInput = Input.GetAxis("Vertical");
//update the position
transform.position = transform.position + new Vector3(0, -1.5f, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Left()
{
//get the Input from Horizontal axis
float horizontalInput = Input.GetAxis("Horizontal");
//get the Input from Vertical axis
float verticalInput = Input.GetAxis("Vertical");
//update the position
transform.position = transform.position + new Vector3(-1.5f, 0, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Right()
{
//get the Input from Horizontal axis
float horizontalInput = Input.GetAxis("Horizontal");
//get the Input from Vertical axis
float verticalInput = Input.GetAxis("Vertical");
//update the position
transform.position = transform.position + new Vector3(1.5f, 0, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Start()
{
var finalLocation = new Vector3(-0.5f, 0.5f, 0);
var currentLocation = transform.position;
while (currentLocation != finalLocation)
{
int randomNum = Random.Range(0, 3);
if (randomNum == 0)
{
Up();
}
else if (randomNum == 1)
{
Down();
}
else if (randomNum == 2)
{
Left();
}
else if (randomNum == 3)
{
Right();
}
}
}
}
UPDATED WORKING CODE:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
void Up()
{
//update the position
transform.position = transform.position + new Vector3(0, 1.5f, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Down()
{
//update the position
transform.position = transform.position + new Vector3(0, -1.5f, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Left()
{
//update the position
transform.position = transform.position + new Vector3(-1.5f, 0, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Right()
{
//update the position
transform.position = transform.position + new Vector3(1.5f, 0, 0);
//output to log the position change
Debug.Log(transform.position);
}
void Update()
{
var finalLocation = new Vector3(2.5f, 2.0f, -2.0f);
var currentLocation = transform.position;
int randomNum = Random.Range(0, 4);
if (currentLocation != finalLocation) {
if (randomNum == 0)
{
Up();
}
else if (randomNum == 1)
{
Down();
}
else if (randomNum == 2)
{
Left();
}
else if (randomNum == 3)
{
Right();
}
return;
}
}
}
My last issue is how I can limit this randomness to only stick to the grid and not go off grid. Any thoughts?

Here are some problems:
horizontalInput and verticalInput are never used inside your functions
while(currentLocation != finalLocation) This condition can fail in some situations. Unity uses float for the coordinates, meaning it needs to be exactly on the same position, every decimal place need to be the same.
Random.Range(0, 3) will return a random number between 0 (inclusive) and 3 (exclusive), so the only possible values will be 0, 1 and 2. The script will never call the Right() function.
Unity uses one thread to run your scripts by default, if you put a while loop to move the object to a location it will freeze the entire game till the object is at the proper place. I recommend you to use the Update() function, it gets called every frame.

Several problems:
Your object is stuck in an endless while loop because it isn't allowed to exit (and do things like render the frame, take input from the user, etc) until your random movement script reaches its finalLocation. You aren't giving the game time to do anything else.
You almost certainly want this to be a coroutine or an Update function.
Random.Range returns an int in the range min (inclusive) to max (exclusive), so your call to it will never return 3.

Related

Move a GameObject to the previous position of other GameObject

I am trying to achieve something similar to the classic snake game, but, the player only moves one unit every time a button is pressed and the "tail (blue square)" needs to move to the previous position of the head
I want the blue square to always stay at the last position of the white square, not on top of it.
Here is my code:
[SerializeField] Transform segmentPrefab;
Vector2 moveInput;
Vector2 currentHeadPos;
//I want to add more segments to the tail so I created a list
List<Transform> segmentList;
void Start()
{
//I add the head as the first object of the list
segmentList = new List<Transform>();
segmentList.Add(transform);
}
void OnMove(InputValue input)
{
//I tried/expected to save the head Pos here and use it to move the segment with this
currentHeadPos = transform.position;
moveInput = input.Get<Vector2>();
//Horizontal movement
if (moveInput.x != 0)
{
transform.position = new Vector2(transform.position.x + moveInput.x, transform.position.y);
}
//Vertical movement
if (moveInput.y != 0)
{
transform.position = new Vector2(transform.position.x, transform.position.y + moveInput.y);
}
//I loop through the list and move the last item to the position in front of it
for (int i = segmentList[segmentList.Count - 1; i > 0, i --]
{
segmentList[i].position = segmentList[i - 1].position;
}
}
//This works fine, is to add new objects to the list
void Connect()
{
Vector3 offset = new Vector3(-1, 0f, 0f);
Transform newSegment = Instantiate(segmentPrefab);
newSegment.position = segmentList[segmentList.Count - 1].position + offset;
segmentList.Add(newSegment);
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.CompareTag("Conector"))
{
Connect();
}
}
I am fairly new to unity and coding and I can't figure out how to avoid the blue square ending on top of the one everytime it moves
I'm going to assume that the segment at segmentList[0] is your white square, and all segments further down the list are your blue squares.
The following lines of code are moving your transform found at segmentList[0]:
//Horizontal movement
if (moveInput.x != 0)
{
transform.position = new Vector2(transform.position.x + moveInput.x, transform.position.y);
}
//Vertical movement
if (moveInput.y != 0)
{
transform.position = new Vector2(transform.position.x, transform.position.y + moveInput.y);
}
If your transform at segmentList[0] has already moved, then when you update segmentList[1] = segmentList[0].position, you have already updated segmentList[0] so it is actually the current position.
If you move the following code to earlier in your OnMove method, you should get your expected results:
//I loop through the list and move the last item to the position in front of it
for (int i = segmentList[segmentList.Count - 1; i > 0, i --]
{
segmentList[i].position = segmentList[i - 1].position;
}

Draw line between two points at runtime using Line Renderer

I'm new to coding and trying to make a game where objects appear on a wall, and players will connect 2 that match via a line (this game will feel 2D but is in fact 3D).
My first step is just to be able to draw a line between 2 points. Later I'll try to figure out whether to use bools, or triggers, or colliders or what to determine whether the player connected the correct objects.
I'm having a lot of trouble with this. I want to click the screen once to determine a starting point, then a second time to determine an end point for the line renderer in unity. Then have this all repeat with a new line.
This script is almost working, but for some reason my first point is always set to (0, 0, 0), and I have no idea why. This is my current script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawLine_2PT : MonoBehaviour
{
// Apply these values in the editor
public LineRenderer Line;
public GameObject newLine;
public float minimumVertexDistance = 0.1f;
public Vector3 GetWorldCoordinate(Vector3 mousePosition)
{
Vector3 mousePos = new Vector3(mousePosition.x, mousePosition.y, 1);
return Camera.main.ScreenToWorldPoint(mousePos);
}
void Start()
{
// set the color of the line
Line.startColor = Color.red;
Line.endColor = Color.red;
// set width of the renderer
Line.startWidth = 0.3f;
Line.endWidth = 0.3f;
Line.positionCount = 0;
}
void Update()
{
if ((Input.GetMouseButtonDown(0)) && Line.positionCount == 0)
{
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
Line.positionCount = Line.positionCount + 2;
}
if (Input.GetMouseButtonDown(0) && Line.positionCount == 2)
{
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
}
The last bit spawns a new line after the 2nd point is made, and turns off the initial line renderer. Again, seems to be working except for the first point always spawning at (0, 0, 0) instead of the mouse position.
Any/all advice is much appreciated.
Thanks
You are doing
Line.positionCount = Line.positionCount + 2;
after you call
Line.SetPosition(0, mousePos);
so this has no effect yet since there are no points yet when you try to set it.
And then both positions you add just keep the default position 0,0,0 until you override the second one later.
You should first increase the count and then set the position.
Also note that actually both your code blocks will be executed in the same frame since after increasing the count the second condition matches as well. Is this intended?
I would rather expect something like e.g.
void Update()
{
if ((Input.GetMouseButtonDown(0))
{
if(Line.positionCount == 0))
{
Line.positionCount = 1;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
}
else
{
Line.positionCount = 2;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
}
And if you want to you could even visually update the line while the user is drawing it like e.g.
void Update()
{
if ((Input.GetMouseButtonDown(0))
{
if(Line.positionCount == 0))
{
Line.positionCount = 1;
Vector3 mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(0, mousePos);
}
else
{
Line.positionCount = 2;
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
Instantiate(newLine, transform.position, Quaternion.Euler(0, 0, 0));
GetComponent<DrawLine_2PT>().enabled = false;
}
}
else if(Line.positionCount == 1)
{
var mousePos = GetWorldCoordinate(Input.mousePosition);
Line.SetPosition(1, mousePos);
}
}

Vector3.Lerp isn't moving the whole way

Basically I'm trying to create a rolling system for my top down RPG style game, but when I do the Vector3.Lerp it doesn't move the entire way
The code is:
public void CheckSpaceKey()
{
if (exhausted == false)
{
if (Input.GetKey(KeyCode.Space) && canRoll)
{
if (rollDir == RollDir.Up)
{
Vector3 rollStartPos = new Vector3(transform.position.x, transform.position.y, -1.0f);
Vector3 rollEndPos = new Vector3(transform.position.x, transform.position.y + 3, -1.0f);
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, Time.deltaTime);
canRoll = false;
playerStats.stamina -= 10;
Invoke("RollCooldown", 1.5f);
}
}
}
}
public void RollCooldown()
{
canRoll = true;
}
This should be making my player move upwards 3 units, but it instead is moving a random number of around 0.35 to 0.45.
Also CheckSpaceKey is being called in update
Lerp doesn't do what you think it does. Assuming Time.deltaTime is about 1/60, it's going to move the unit 1/60th of the way to the destination, and that's it.
Consider using a coroutine that updates the t parameter for Lerp with each frame:
public void CheckSpaceKey()
{
// by the way, this could just be `if (!exhausted)`
// although some coding guidelines require this format below
if (exhausted == false)
{
if (Input.GetKey(KeyCode.Space) && canRoll)
{
if (rollDir == RollDir.Up)
{
StartCoroutine(DoRoll(Vector3.up * 3));
}
}
}
}
IEnumerator DoRoll(Vector3 offset)
{
float duration = 1f;
canRoll = false;
playerStats.stamina -= 10;
Invoke("RollCooldown", 1.5f);
Vector3 rollStartPos = new Vector3(transform.position.x, transform.position.y, -1.0f);
Vector3 rollEndPos = rollStartPos + offset;
float t = 0;
while (t < 1f)
{
t = Mathf.Min(1f, t + Time.deltaTime/duration);
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, t);
yield return null;
}
}
public void RollCooldown()
{
canRoll = true;
}
This will not move 3 Units, you call
transform.position only inside the if case and access the if case only once every 1.5 seconds.
Lerp will not update your position every frame but only the frame it is called, if you the object to move constantly you have to update your position constantly you should crate rollStartPos and rollEndPos inside the first if and create add
else{
time += Time.deltaTime;
transform.position = Vector3.Lerp(rollStartPos, rollEndPos, time);
}
under the 2nd if. This way it will lerp til rollEndPos unless the ball(?) is exhausted, if it should roll even when it's exhausted you need to update the position outside the first if

why wont "transform.position = new Vector3(x,y,z)" reset my object's position?

I'm trying to get an object to move along a path and reset/move back to a starting position, ready to move down the path again.
The line in question reads sign_obj.transform.position = startpos; but it doesn't work even tho the debug line after it is executed!
(I'd pull out my hair if I had any!)
here's the code in full;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/* SignController
*
* controls the placement of and movement of signs
*
* by: Maurice Thompson-Hamilton
* */
public class SignController : MonoBehaviour {
public GameObject sign_center;
public GameObject sign_left;
public GameObject sign_right;
public float sign_distance;
public float sign_speed;
public bool enableSigns = true;
public Vector3 startPos = new Vector3();
private List<GameObject> hazards = new List<GameObject>();
private Vector3 sign_move;
// Use this for initialization
void Start () {
// starting pos of signs
if (startPos == Vector3.zero) { startPos = new Vector3(-12.5f, 14f, 165f); }
// set move distance if not set
if (sign_distance == 0) { sign_distance = 5; }
// set move speed if not set
if (sign_speed == 0) { sign_speed = 1.25f; }
//GameObject tmp = sign_center;
sign_center.transform.Rotate(new Vector3(0, 90f, 0));
sign_left.transform.Rotate(new Vector3(0, 90f, 0));
sign_right.transform.Rotate(new Vector3(0, 90f, 0));
sign_center.transform.position = startPos;
//Debug.Log(string.Format("sign_center {0}", startPos));
startPos.z += sign_distance;
sign_left.transform.position = startPos;
//Debug.Log(string.Format("sign_left {0}", startPos));
startPos.z += sign_distance;
sign_right.transform.position = startPos;
//Debug.Log(string.Format("sign_right {0}", startPos));
// add signs to list
hazards.Add(Instantiate(sign_center, startPos, Quaternion.identity) as GameObject);
hazards.Add(Instantiate(sign_left, startPos, Quaternion.identity) as GameObject);
hazards.Add(Instantiate(sign_right, startPos, Quaternion.identity) as GameObject);
// Debug.Log(string.Format("number of signs :{0}", hazards.Count));
}
// Update is called once per frame
void Update () {
//debug - test movement
//hazards[0].transform.Translate (Vector3.forward);
if (enableSigns) {
Vector3 tmp = new Vector3(0, 0, 0);
Vector3 player_pos = GameObject.FindGameObjectWithTag("PlayerTag").transform.position;
Debug.Log(player_pos.ToString());
// sign counter
int sc = hazards.Count;
if (sc > 0) {
foreach (GameObject sign_obj in hazards) {
// get current sign's pos
tmp = sign_obj.transform.position;
// get sample of new pos
tmp.z -= sign_distance;
// test if sample pos would move sign past player's view
//if (player_pos.z > tmp.z) {
int past_player = player_pos.z.CompareTo(tmp.z);
//Debug.Log(string.Format("past_player={0}, player_pos={1}, tmp.z={2}", past_player, player_pos.z, tmp.z));
if (past_player > 0) {
// if so, set translation vector
sign_move = new Vector3(0, 0, (float)sign_distance * sign_speed * Time.deltaTime);
// move sign
sign_obj.transform.Translate(sign_move);
} else {
// otherwise, reset sign pos to starting pos
sign_obj.transform.position = startPos;
Debug.Log(string.Format("startpos#{0}, sign_obj#{1}", startPos, sign_obj.transform.position));
//tmp.z = 165f;
/*
Destroy(sign_obj);
hazards.Remove(sign_obj);
*/
}
//sign_obj.transform.position = tmp;
//Debug.Log(string.Format("player#{0} - tmp#{1} - sign#{2}",player_pos, tmp, sign_obj.transform.position));
/* if visible
* - move towards player camera
* - if behind player
* - - remove sign (Destroy?)
* else
* */
}
}
}
}
}
I have left in the various tests (commented out) to see where the issue is. Copy, paste and un-comment as necessary.
I'm assuming that you want the signs' initial Z position to be greater than the players' initial Z position, and that the signs must move towards the player.
First thing you gotta do is correct the sign_speed in your code: it should be negative to move towards the players (lower) Z position.
if (sign_speed == 0) { sign_speed = -1.25f; }
Second thing is that you swapped the logic when calculating if the signs are past the player.
int past_player = tmp.z.CompareTo(player_pos.z);
If my assumptions are the inverse of what you want, just adapt my answer according to your needs: verify if the speed is set to the right (positive or negative) value, and check if the past_player comparison is being done right.

Rotating avatar at certain x coordinate Unity

I have a problem with turning my avatar at a certain position on the map. The character moves back and forth, but he doesn't want to rotate at coordinates i entered. What am i doing wrong?
this is my code:
sing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
transform.position = new Vector3(PingPong (Time.time * speed, startXCord, endXCord ), transform.position.y, transform.position.z);
if (transform.position.x == startTurnx ) {
Debug.Log("start Check");
obj.transform.Rotate(0f, 180f, 0f);
}
if (transform.position.x == endTrunx ) {
obj.transform.Rotate(0f, 180f, 0f);
Debug.Log("einde check");
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}
I believe the problem is that you're trying to flip your avatar once he reaches a particular x-coordinate, but he may never reach that EXACT coordinate. if (transform.position.x == startTurnx) will only return true if the two values are EXACTLY the same, and your avatar isn't actually moving smoothly across the screen. He's actually jumping minute amounts every frame, so he may never land on exactly that point.
Instead, my recommendation would be to compare his new position against his old position to see which direction he's traveling in and flip him when he changes direction. Some code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
//I'm going to assume you start it moving left. You may have to change this
bool goingLeft = false;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
float prevX = transform.position.x;
float newX = PingPong (Time.time * speed, startXCord, endXCord );
transform.position = new Vector3(newX, transform.position.y, transform.position.z);
if (newX > prevX) {
//avatar is moving to the right, check to see if that's the direction it was going last Update
if (goingLeft) {
Debug.Log("Flipping Right");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = false;
}
}else if (newX < prevX){
//avatar is moving to the left, check to see it that's the direction it was going last Update
if (!goingLeft) {
Debug.Log("Flipping Left");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = true;
}
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}

Categories