I've just started learning Unity, and I wanted to try to write a Snake.
Logic goes as follows:
Head marks its spot
Head moves
Loop starts
Child goes to marked spot
Spot where child was gets marked
This should have made elements of the snake follow each other, however, it results in weird shapes:
Here's snake's starting position
And here is snake's position after pressing "w"
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
public class Movement : MonoBehaviour
{
int fps = 60;
Vector3 prev_pos, mark_pos;
void Start()
{
Application.targetFrameRate = fps;
}
void Update_child()
{
foreach(Transform child in transform)
{
prev_pos = child.position;
child.position = mark_pos;
mark_pos = prev_pos;
print(child.position);
print(child.name);
}
}
void Update()
{
mark_pos = transform.position;
if (Input.GetKeyDown("w"))
{
transform.position += new Vector3(0, 1, 0);
Update_child();
}
else if (Input.GetKeyDown("a"))
{
transform.position += new Vector3(-1, 0, 0);
Update_child();
}
else if (Input.GetKeyDown("s"))
{
transform.position += new Vector3(0, -1, 0);
Update_child();
}
else if (Input.GetKeyDown("d"))
{
transform.position += new Vector3(1, 0, 0);
Update_child();
}
}
}
I firstly want to understand the mistake, not to get the fixed version of the code, if someone could explain the error, I would appreciate it.
I tried doing this with relative and absolute postions, as both of them are used, but to no avail.
Your answer was almost correct but in your hierarchy you make the tail a children of the head and in Unity, when you move a parent, all of the children move too. To fix your issue, uncouple the tail and track the transforms.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
int fps = 60;
Vector3 prev_pos, mark_pos;
[Header("The head of the snake")]
[SerializeField] Transform headTransform;
[Header("The tail of the snake")]
[SerializeField] Transform[] transforms;
void Start()
{
Application.targetFrameRate = fps;
}
void Update_child()
{
foreach (Transform child in transforms)
{
prev_pos = child.position;
child.position = mark_pos;
mark_pos = prev_pos;
print(child.position);
print(child.name);
}
}
void Update()
{
mark_pos = headTransform.position;
if (Input.GetKeyDown("w"))
{
headTransform.position += new Vector3(0, 1, 0);
Update_child();
}
else if (Input.GetKeyDown("a"))
{
headTransform.position += new Vector3(-1, 0, 0);
Update_child();
}
else if (Input.GetKeyDown("s"))
{
headTransform.position += new Vector3(0, -1, 0);
Update_child();
}
else if (Input.GetKeyDown("d"))
{
headTransform.position += new Vector3(1, 0, 0);
Update_child();
}
}
}
Related
I have a canvas with three text objects in unity. i have 4 scenes: Load, Play, Hub, and Tutorial.
I'm using a Script Object that keeps track of your scores as you play. But, every time I set a high score, my text objects still say "1st" "2nd" "3rd." Here's my script: (Also, in play, the text for my score updates, but the (Highscores) is not updating between scenes. And the object the script is on is parenting all except for Camera.Main)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class TrackScores : MonoBehaviour
{
public Transform First;
public Transform Second;
public Transform Third;
public Text Best;
public Text Average;
public Text Worst;
public Text Score;
public Transform ScoreTracker;
public Canvas CanvUI;
public float ScoreInt;
public Vector3 PosPlayer;
void Start()
{
DontDestroyOnLoad(Camera.main.transform.root.gameObject);
DontDestroyOnLoad(transform.root.gameObject);
SceneManager.LoadScene("Hub", LoadSceneMode.Single);
}
void Update()
{
if(SceneManager.GetActiveScene().name == "Play")
{
PosPlayer = new Vector3(GameObject.Find("Player").transform.position.x, GameObject.Find("Player").transform.position.y, 0);
Camera.main.transform.position = PosPlayer + new Vector3(0, 0, -15);
transform.position = new Vector3(0, 0, 1001);
Score = GameObject.Find("Score").GetComponent<Text>();
ScoreTracker = GameObject.Find("ScoreTracker").transform;
ScoreInt = ScoreTracker.position.x * 1000;
Score.text = ScoreInt.ToString("0");
}
if(SceneManager.GetActiveScene().name == "Tutorial")
{
PosPlayer = new Vector3(GameObject.Find("Quad").transform.position.x, GameObject.Find("Quad").transform.position.y, 0);
Camera.main.transform.position = PosPlayer + new Vector3(0, 0, -15);
transform.position = new Vector3(0, 0, -1000);
}
if(SceneManager.GetActiveScene().name == "Hub")
{
Camera.main.transform.position = new Vector3(0, 0, -25);
transform.position = new Vector3(0, 0, 0);
Best.text = "1st: " + First.position.x.ToString("0");
Average.text = "2nd: " + Second.position.x.ToString("0");
Worst.text = "3rd:" + Third.position.x.ToString("0");
if(ScoreInt < Second.position.x && ScoreInt > Third.position.x)
{
Third.position = new Vector3(ScoreInt, 0, 0);
}
if(ScoreInt < First.position.x && ScoreInt > Second.position.x)
{
Second.position = new Vector3(ScoreInt, 0, 0);
}
if(ScoreInt > First.position.x)
{
First.position = new Vector3(ScoreInt, 0, 0);
}
}
}
}
Any help is appreciated!
I figured it out. i just need to use text mesh with TextMesh UI.
I have such a problem, maybe someone will help. I am making a game and I need to make a dice that goes in the direction opposite to the click. For example, clicking on the back of a cube moves it forward, etc. Gravity must act on the cube as shown in the figure below. Unfortunately, the cube does not change height, but only the position of X and Z. Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCube : MonoBehaviour
{
Vector3 movePosition;
public float time = 0.02f;
[HideInInspector]
public bool blockClick = false;
private void Start()
{
movePosition = transform.position;
}
private void Update()
{
blockClick = false;
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit raycastHit;
if (Physics.Raycast(ray, out raycastHit))
{
string hitName = raycastHit.transform.name;
if (hitName == "MoveCube")
{
move(raycastHit);
blockClick = true;
}
}
}
transform.position = Vector3.Lerp(
transform.position,
movePosition,
time);
}
public void move(RaycastHit raycastHit)
{
Vector3 incomingVec = raycastHit.normal - Vector3.up;
// South
if (incomingVec == new Vector3(0, -1, -1))
{
movePosition = movePosition + new Vector3(0, 0, 1);
return;
}
// North
if (incomingVec == new Vector3(0, -1, 1))
{
movePosition = movePosition + new Vector3(0, 0, -1);
return;
}
// West
if (incomingVec == new Vector3(-1, -1, 0))
{
movePosition = movePosition + new Vector3(1, 0, 0);
return;
}
// East
if (incomingVec == new Vector3(1, -1, 0))
{
movePosition = movePosition + new Vector3(-1, 0, 0);
return;
}
}
}
From what I know, changing the transform.position of a gameObject with a rigidbody will mess with Unity's physics, and it won't work as intended. I suggest trying to apply a force from the position of the mouse's click instead of moving the cube's position manually (if this makes sense to do for your purposes). Here is the documentation for Rigidbody.AddForce
I am developing a Tetris game. And I have a problem with an issue. I just implemented the Spawn of the pieces and automatic tumbling. However, when I went to test the collision on the wall and on the floor, the object is teleporting to the other side, as shown in the gif below.
I used Rigidbody and Box Collider with OnTriggerEnter and OnTriggerExit. To detect the collision and not cross the wall. Before implementing Spawn and the others, everything was working normally. Now, the object is teleporting when collid in the wall. What did I end up doing to make this happen? Here is code below before and after implementation.
Code before implementation:
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;
using UnityEngine;
using Debug = UnityEngine.Debug;
[RequireComponent(typeof(Rigidbody))]
public class Movimentacao : MonoBehaviour
{
public bool Rotation;
public bool Rotation360;
bool collidedRight = true;
bool collidedLeft = true;
bool collidedBottom = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
transform.position += new Vector3(0.6775f, 0, 0);
if (!collidedRight) {
transform.position += new Vector3(-0.6775f, 0, 0);
}
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
transform.position += new Vector3(-0.6775f, 0, 0);
if (!collidedLeft){
transform.position += new Vector3(0.6775f, 0, 0);
}
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
transform.position += new Vector3(0, -0.6775f, 0);
if (!collidedBottom){
transform.position += new Vector3(0, 0.6775f, 0);
}
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (Rotation)
{
if (!Rotation360)
{
if (transform.rotation.z < 0)
{
if (collidedBottom)
{
transform.Rotate(0, 0, 90);
}
}
else
{
if (collidedBottom)
{
transform.Rotate(0, 0, -90);
}
}
}
else
{
if (collidedBottom)
{
transform.Rotate(0, 0, -90);
}
}
}
}
}
void OnTriggerEnter(Collider other)
{
if (Mathf.Sign(other.transform.position.x) == 1)
{
collidedRight = false;
}
if (Mathf.Sign(other.transform.position.y) == -1)
{
collidedBottom = false;
}
if (Mathf.Sign(other.transform.position.x) == -1)
{
collidedLeft = false;
}
}
void OnTriggerExit(Collider other)
{
if (!collidedRight)
{
collidedRight = true;
}
if (!collidedBottom)
{
collidedBottom = true;
}
if (!collidedLeft)
{
collidedLeft = true;
}
}
}
Code After Implementation:
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Threading;
using UnityEngine;
using Debug = UnityEngine.Debug;
[RequireComponent(typeof(Rigidbody))]
public class Movimentacao : MonoBehaviour
{
public bool Rotation;
public bool Rotation360;
public float queda;
public float velocidade;
public float timer;
bool collidedRight = true;
bool collidedLeft = true;
bool collidedBottom = true;
// Start is called before the first frame update
void Start()
{
timer = velocidade;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyUp(KeyCode.RightArrow) || Input.GetKeyUp(KeyCode.LeftArrow) || Input.GetKeyUp(KeyCode.DownArrow))
timer = velocidade;
if (Input.GetKey(KeyCode.RightArrow))
{
timer += Time.deltaTime;
if (timer > velocidade)
{
transform.position += new Vector3(0.6775f, 0, 0);
timer = 0;
}
if (!collidedRight) {
transform.position += new Vector3(-0.6775f, 0, 0);
}
}
if (Input.GetKey(KeyCode.LeftArrow))
{
timer += Time.deltaTime;
if (timer > velocidade)
{
transform.position += new Vector3(-0.6775f, 0, 0);
timer = 0;
}
if (!collidedLeft){
transform.position += new Vector3(0.6775f, 0, 0);
}
}
if (Input.GetKey(KeyCode.DownArrow))// || Time.time - queda >= 1)
{
timer += Time.deltaTime;
if (timer > velocidade)
{
transform.position += new Vector3(0, -0.6775f, 0);
timer = 0;
}
if (!collidedBottom){
transform.position += new Vector3(0, 0.6775f, 0);
}
//queda = Time.time;
}
if (Time.time - queda >= 1 && !Input.GetKey(KeyCode.DownArrow))
{
transform.position += new Vector3(0, -0.6775f, 0);
if (!collidedBottom)
{
transform.position += new Vector3(0, 0.6775f, 0);
}
queda = Time.time;
}
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (Rotation)
{
if (!Rotation360)
{
if (transform.rotation.z < 0)
{
if (collidedBottom)
{
transform.Rotate(0, 0, 90);
}
}
else
{
if (collidedBottom)
{
transform.Rotate(0, 0, -90);
}
}
}
else
{
if (collidedBottom)
{
transform.Rotate(0, 0, -90);
}
}
}
}
}
void OnTriggerEnter(Collider other)
{
if (Mathf.Sign(other.transform.position.x) == 1)
{
collidedRight = false;
}
if (Mathf.Sign(other.transform.position.y) == -1)
{
collidedBottom = false;
}
if (Mathf.Sign(other.transform.position.x) == -1)
{
collidedLeft = false;
}
}
void OnTriggerExit(Collider other)
{
if (!collidedRight)
{
collidedRight = true;
}
if (!collidedBottom)
{
collidedBottom = true;
}
if (!collidedLeft)
{
collidedLeft = true;
}
}
}
So I'm just gonna start off with the solution and I'll explain why I did each step below:
Instead of checking for collisions using triggers, try disabling your colliders "IsTrigger" property on the cube and walls (In the inspector).
Next on your Rigidbody make sure "Use Gravity" and "Is Kinematic" are also off on the cube (In the inspector).
Finally, In the Rigidbody, under constraints freeze all rotation directions (In the inspector).
Explanation:
Colliders in Unity can be either well, a normal collider that interacts with other colliders or it can be a trigger. Triggers a normally used when you need to know when something happens without known exactly when it will happen. In the case of your project here, you could use colliders but as you saw it quickly complicated your code. Instead, it's a better choice to let the colliders, well collide, and do their thing. The Rigidbody component adds more functionality to your colliders allowing them to be stopped by other colliders in your scene. This is perfect for your walls as when we try to move passed them we get pushed back out by the Rigidbody. This removes the need for collision checks as the Rigidbody already handles it.
We turn off the "Use Gravity" property because as the name implies, it will cause the object to be affected by gravity which isn't something we want. We also turn off the "IsKinematic" because we want the object to respond to collisions with other colliders.
We freeze all the rotations in the Rigidbody since we don't want the physics engine to accidentally start rotating our block if we hit another object. We of course can still rotate the block via scripting by adjusting the transform as you've already been doing. We, however, don't want to freeze out position since we want the Rigidbody to stop us from going through other colliders.
Some Final Closing Notes:
If adjust the blocks transform by too much "transform.position += someNewPosition;" you could accidentally end up on the other side of the wall. This happens since you moved the block pretty much completely passed the wall in one frame so the Rigidbody didn't have a chance to stop it. If this happens try either thickening up the walls or making the movement distance smaller.
In your code, it looks like you are trying to move the block after some time passes. If this is what you are trying to do, you should probably consider moving the "timer += Time.deltaTime;" lines you have everwhere to the top of the Update method. Because "Time.deltaTime" only gives you the time between frames we should always add this to our ongoing timer every frame. This will allow the timer to count in real-time as each frame is called. It is already reset after every move which makes this an easy implementation.
As for an answer as to why the weirdness you saw happens occurred. I can't for 100% give you a correct answer but it most likely has to do with your trigger implementation since you set a bool in the OnTriggerEnter method and wait for the Update method to fix the issue. Instead, if you choose to continue with the trigger idea or for future reference, you should get everything you need to get done (like fix the block's location) all inside the OnTriggerEnter method since you know there's an issue already when that method is called.
Hope this helps you out. Cheers.
I am trying to use Main Camera position to make an appear and disappear. For example, if the camera.main.transform.position = (0,2,0); make the object appear otherwise make it disappear.
The object in this case a basic Cube. I started using setActive function but as it turns out once you have setActive as false, Update function on the specific object does not run. I have added the script I was using:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class backandforth : MonoBehaviour
{
public float speed = 2.5f;
GameObject targetObject;
// Use this for initialization
void Start()
{
targetObject = GameObject.Find("Cube");
targetObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
//move the cube from (0,0,0)
if (Camera.main.transform.position== new Vector3(0, 2, 0)) {
transform.position = new Vector3(Mathf.PingPong(Time.time * speed, 5), transform.position.y, transform.position.z);
transform.Rotate(0, 0, 5);
}
else
{
targetObject.SetActive(true);
transform.position = new Vector3(Mathf.PingPong(Time.time * speed, 5), transform.position.y, transform.position.z);
transform.Rotate(0, 0, 100);
//gameObject.SetActive(false);
}
}
}
Here is the hierarchy view of the setup to make the GameObject definition clear.
Any suggestion on how do I go about doing this? Thanks!
If I understand correctly the update method should be like this:
void Update()
{
if (Camera.main.transform.position== new Vector3(0, 2, 0)) {
//if the camera.main.transform.position = (0,2,0); make the object appear
targetObject.SetActive(true);
}
else
{
//otherwise make it disappear
targetObject.SetActive(false);
}
}
I have a camera that I control it with the (W,A,S,D) keys...
What I want to do is that when press left mouse click ("Fire1") the camera turns back to the first position animatedly.
Is it possible to do it with Mecanim and create a dynamic animation file to do it?!
That's my code:
void Update ()
{
if (Input.GetKey(KeyCode.W))
{
Cam.transform.Rotate (0, 0, 2);
}
if (Input.GetKey(KeyCode.S) )
{
Cam.transform.Rotate (0, 0, -2);
}
if (Input.GetKey(KeyCode.D))
{
Cam.transform.Rotate (0, 2, 0);
}
if (Input.GetKey(KeyCode.A))
{
Cam.transform.Rotate (0, -2, 0);
}
My camera position and rotation at the start is (0,0,0) but when I control my camera these parameters change so I want my camera to turns back to the first position (0,0,0) animatedly when I press left mouse button...
Something like:
if (Input.GetButtonDown("Fire1"))
{
Cam.GetComponent<Animation> ().Play ();
}
Instead of the animation, you could smooth out the camera movement:
Add the following variables to your script, the first one is used to control the amount of smoothness you want:
public float smoothTime = 0.2f;
private Vector3 velocity = Vector3.zero;
And then:
if (Input.GetButtonDown("Fire1")) {
Vector3 targetPosition = new Vector3(0,0,0);
Cam.transform.position = Vector3.SmoothDamp(Cam.transform.position, targetPosition, ref velocity, smoothTime);
}
From your code, I can see that you are only changing rotation of the Camera. Following is my solution to it.
It saves the starting rotation at the start and later lerps to the start rotation when "Fire1" is pressed.
However, position is not handled here, as there's no position change in your code. But the concept is same. You can change the position in a similar way.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamTest : MonoBehaviour {
public float animSpeed = 1.0f;
public Camera Cam;
private Quaternion startRotation;
private bool doRotate = false;
// Use this for initialization
void Start () {
//Cam = GetComponent<Camera> ();
startRotation = transform.rotation;
}
void Update () {
if (Input.GetKey(KeyCode.W))
{
Cam.transform.Rotate (0, 0, 2);
}
if (Input.GetKey(KeyCode.S) )
{
Cam.transform.Rotate (0, 0, -2);
}
if (Input.GetKey(KeyCode.D))
{
Cam.transform.Rotate (0, 2, 0);
}
if (Input.GetKey(KeyCode.A))
{
Cam.transform.Rotate (0, -2, 0);
}
if (Input.GetButtonDown("Fire1")) {
Debug.Log ("Fire1");
doRotate = true;
}
if(doRotate) DoRotation ();
}
void DoRotation(){
if (Quaternion.Angle(Cam.transform.rotation, startRotation) > 1f) {
Cam.transform.rotation = Quaternion.Lerp(Cam.transform.rotation, startRotation,animSpeed*Time.deltaTime);
} else {
Cam.transform.rotation = startRotation;
doRotate = false;
}
}
}