Object teleporting to another posisiton - Unity3D - c#

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.

Related

EnemyAI script not moving the Enemy

I am making my first foray into AI, specifically AI that will follow the player.
I am using the A* Path finding project scripts, but used the Brackeys tutorial for the code
https://www.youtube.com/watch?v=jvtFUfJ6CP8
Source in case needed.
Here's the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public class EnemyAI : MonoBehaviour
{
public Transform Target;
public float speed = 200f;
public float NextWayPointDistance = 3f;
Path path;
int currentWaypoint = 0;
bool ReachedEndOfpath = false;
Seeker seeker;
Rigidbody2D rb;
public Transform EnemyGraphics;
// Start is called before the first frame update
void Start()
{
seeker = GetComponent<Seeker>();
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("UpdatePath", 0f, 1f);
}
void UpdatePath()
{
if (seeker.IsDone())
seeker.StartPath(rb.position, Target.position, OnPathComplete);
}
void OnPathComplete(Path p)
{
if (!p.error)
{
path = p;
currentWaypoint = 0;
}
}
// Update is called once per frame
void fixedUpdate()
{
if(path == null)
return;
if(currentWaypoint >= path.vectorPath.Count)
{
ReachedEndOfpath = true;
return;
}
else
{
ReachedEndOfpath = false;
}
Vector2 Direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized;
Vector2 Force = Direction * speed * Time.fixedDeltaTime;
rb.AddForce(Force);
float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]);
if(distance < NextWayPointDistance)
{
currentWaypoint++;
}
if(rb.velocity.x >= 0.01f)
{
EnemyGraphics.localScale = new Vector3(-1f, 1f, 1f);
}else if(rb.velocity.x <= 0.01f)
{
EnemyGraphics.localScale = new Vector3(1f, 1f, 1f);
}
}
}
How I tried to fix the issue:
I thought it could've been a problem with the speed so I increased it to 10000000, and still nothing
Next I thought it was a problem with the Rigidbody2d so I check there, found the gravity scale was set at 0, so I increased it to 1. It made my enemy fall to the ground but still no movement.
I thought it could've been a problem with the mass and drag, so I set Linear drag and Angular drag to 0, and also set mass to 1. Still nothing.
I set the body type to kinematic, pressed run, nothing. Set the body type to static, pressed run, nothing. Set the body type to Dynamic, pressed run, still nothing.
I tried to make a new target for the enemy to follow, dragged the empty game object i nto the target, pressed run and still didn't move.
I am at a loss on how to fix this.
Please help?
Looks like maybe a typo? You have:
// Update is called once per frame
void fixedUpdate()
{
but the method is called FixedUpdate(), with a big F in front. You have fixedUpdate, which is NOT the same.

2d Platformer game stone changing size

I am trying to make a platformer game. In the platformer game has a moving platform that makes everything it touches its transform's child while it touches it. Interestingly, whenever a stone(A movable object with a rigidbody and a polygon collider) touches the moving platform, the stone's scale goes haywire. Even though the scale reads the same on the transform component, it appears to be larger or smaller than it really is when touching it. When it stops touching the platform, the stone appears normal. Can anyone help me. Thank you.
This is the moving platform script that moves the platform around.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveTwoTransforms : MonoBehaviour
{
public Transform pointA;
public Transform pointB;
public bool HasReachedA;
public bool HasReacedB;
// Start is called before the first frame update
void Start()
{
transform.position = pointA.position;
HasReacedB = true;
HasReachedA = false;
StartCoroutine(GlideAround());
}
// Update is called once per frame
void Update()
{
}
public IEnumerator GlideAround()
{
while (true)
{
while (HasReachedA == false)
{
yield return new WaitForEndOfFrame();
transform.position = Vector2.Lerp(transform.position, pointA.position, 0.01f);
if ((Mathf.Abs(Vector2.Distance(pointA.position, transform.position)) < 0.01f))
{
HasReacedB = false;
HasReachedA = true;
}
}
while (HasReacedB == false)
{
yield return new WaitForEndOfFrame();
transform.position = Vector2.Lerp(transform.position, pointB.position, 0.01f);
if ((Mathf.Abs(Vector2.Distance(pointB.position, transform.position)) < 0.01f))
{
HasReacedB = true;
HasReachedA = false;
}
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if((collision.gameObject.tag == "Stone"|| collision.gameObject.tag == "Player") && (collision.transform.position.y - collision.transform.lossyScale.y / 2 >= transform.position.y))
{
collision.transform.parent = transform;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
collision.transform.parent = null;
}
}
This is the stone script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RememberPositions : MonoBehaviour
{
public Vector3 StartingPosition;
public Vector3 StartingRotation;
public Vector3 StartingScale;
float StartRotation;
// Start is called before the first frame update
void Start()
{
StartingPosition = new Vector3(transform.position.x, transform.position.y, 0);
StartingRotation = new Vector3(0, 0, transform.position.z);
StartingScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
// Update is called once per frame
void Update()
{
transform.localScale = StartingScale;
}
}
There are no errors whatsoever and the rigidbody seems to be changing to the shape of the stone. Can anyone please specify the correct code I should use? Thank you.
If your moving platform is scaled, your stone will get scaled too. There are workarounds shown here to avoid this issue. One of them is adding a child GameObject to the platform, and moving the collision handling from the platform to that child.

Unity 5: Rigidbody 2d sticking to ceiling while velocity is moving upwards

I am making a 2d game. My problem is that while playing, if the player holds jump and is under a BoxCollider2D, the player will not fall until they release jump.
My player GameObject consists of a sprite renderer, a dynamic rigidbody2d with gravity on and a boxcollider2d.
Here are my movement scripts:
1:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpScript : MonoBehaviour {
[Range(1, 10)]
public float jumpVelocity;
[Range(0,10)]
public float speed;
private bool jumpQueue = false;
private bool boolin=false;
void Update()
{
//Friggin fall, loser
//Jumping
///*
if (Input.GetButton("Jump")&& GetComponent<Rigidbody2D> ().velocity.y==0)
{
GetComponent<Rigidbody2D>().velocity = Vector2.up * jumpVelocity;
}
//*/
//jumpQueue?
/*
if(Input.GetButtonDown("Jump"))
{
jumpQueue = true;
}*/
//Right Movement
if (Input.GetKey(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if(Input.GetKeyUp(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Left Movement
if (Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(-1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if (Input.GetKeyUp(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//No movement?
if (Input.GetKey(KeyCode.D) && Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Time to handle animation, boios.
Rigidbody2D rb = GetComponent<Rigidbody2D>();
bool schwomp = false;
bool schwift = false;
if(rb.velocity.y>0)
{
schwomp = true;
}
if(rb.velocity.y<0)
{
schwift = true;
}
Animator anim = GetComponent<Animator>();
if (boolin)
{
anim.SetInteger("Boolin", 1);
/*if (!anim.GetBool("expand"))
{
anim.SetBool("expand", true);
anim.Play("running");
}*/
}
else
{
anim.SetInteger("Boolin", 0);
/*
if(anim.GetBool("expand"))
{
anim.SetBool("expand", false);
anim.Play("Idle");
}*/
}
if(schwomp)
{
//anim.SetInteger("Boolin", 2);
}
if(schwift)
{
//anim.SetInteger("Boolin", 3);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BetterJumper : MonoBehaviour {
public float fallMultiplier = 2.5f;
public float lowJumpMultiplier = 2f;
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if(rb.velocity.y<0)
{
rb.velocity += Vector2.up*Physics2D.gravity.y*(fallMultiplier-1)*Time.deltaTime;
}
else if(rb.velocity.y>0&&!Input.GetButton("Jump"))
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
}
}
}
Thank you so much in advance!
You're using Input.GetKey() which will poll the key every frame. This means more and more velocity is added the longer you hold jump. You've effectively built a jetpack rather than a jump force.
You should use Input.GetKeyDown() which will only fire once when a key is pressed down, then has to be released and pressed again in order to re-trigger. You then need to apply a single sufficiently strong vertical force using RigidBody.AddForce() to make the character jump, rather than adding continuously to the velocity.
Additionally, you should really be caching the result of your GetComponent<Rigidbody2D>() call when the script either wakes up or starts so that you're not calling it continuously; Each one of those calls takes processing time. Also, you should be using FixedUpdate() for physics.
public class ExampleClass : MonoBehaviour {
public float thrust;
public Rigidbody rb; // make the rigidbody variable available anywhere in the class
void Start() {
// cache the rigidbody component to the variable once on start
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
// use the variable reference from now on rather than making GetComponent calls
rb.AddForce(transform.up * thrust);
}
}
Soviut's answer explains it quite nicely, but there's more that you need to know. You're directly manipulating the velocity, which overrides the effect of any forces, including gravity(despite the fact you're applying it manually). See the documentation.
As demonstrated in Soviut's answer you should be applying forces and impulses, letting the physics engine determine the velocity. The only time you should set velocity directly is when you're building a simulation that intentionally has unrealistic physics(i.e. retro platformers). Even in that case, bear in mind that doing so creates a lot more work, because you'll need to factor in every little thing that creates movement. This means you're essentially re-inventing the physics engine.

Move doors in unity3d, at specific points through script C#

I have two doors in my game. Initially they are closed. After that they open and will stop at specific points (pic attached as a sample). So far I have written a script, which rotates the door continuously. I want to stop them, like at 45 angle, need kind suggestion.
using UnityEngine;
using System.Collections;
public class rotate : MonoBehaviour
{
public string rotate_along = "y";
public float speed = 10.0f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
if (rotate_along == "y") {
this.transform.Rotate (0, speed, 0);
} else if (rotate_along == "x") {
this.transform.Rotate (speed * Time.deltaTime, 0, 0);
} else if (rotate_along == "z") {
this.transform.Rotate (0, 0, speed * Time.deltaTime);
} else {
print ( "please! check your cordinate for rotating for "+gameObject.name );
}
}
}
I suggest you to use Lerp or Slerp for this:
void Update() {
transform.rotation = Quaternion.Lerp(from.rotation, to.rotation, Time.time * speed);
}
[Source]
This will move your doors naturally smoothly like in real world and will let you avoid coding horror in vector space. Unity community has pretty much a lot of examples how quaternions works. Here you can find brief explanation of what is difference:
http://answers.unity3d.com/questions/389713/detaliled-explanation-about-given-vector3slerp-exa.html
You are rotating the doors at a constant velocity without setting a maximum angle..
Something like this should do the trick.. But I haven't been able to test it.
using UnityEngine;
using System.Collections;
public class rotate : MonoBehaviour
{
public string rotate_along = "y";
public float speed = 10.0f;
private float _currentAngle = 0.0f;
private float _targetAngle = 45.0f;
private float _completed = false;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (_completed)
return;
float angle = speed * Time.deltaTime
if (_currentAngle + angle > _targetAngle)
{
angle = _targetAngle - _currentAngle;
_completed = true;
}
if (rotate_along == "y")
{
this.transform.Rotate (0, angle, 0);
}
else if (rotate_along == "x")
{
this.transform.Rotate (angle, 0, 0);
} else if (rotate_along == "z")
{
this.transform.Rotate (0, 0, angle);
}
else
{
print ( "please! check your cordinate for rotating for "+gameObject.name );
}
_currentAngle += angle;
}
}

Why can't I jump, or jump well?

Right now I'm not getting much Out of this. Depending on what I do different i either end up with an infinite loop, or poor jumping ability. I used a timer to tick my jumped bool but I was getting a double like jump ability and my ground detection wasn't good enough. Can you See why I can't jump, or jump well?
using UnityEngine;
using System.Collections;
public class player : MonoBehaviour
{
public float speed = 0.05f;
public float jumpVelocity = 0.05f;
public bool onGround = true;
public bool jumped = false;
// Use this for initialization
void Start () { }
// Update is called once per frame
void Update ()
{
//test if player is on the ground
if (onGround) { jumped = false; }
// USER CONTROLS HERE.
if (Input.GetKeyDown(KeyCode.Space) && onGround == true)
{
jumped = true;
while(jumped)
{
this.rigidbody2D.AddForce(new Vector2(0, jumpVelocity));
if (onGround) { jumped = false; }
}
}
else if (Input.GetKey(KeyCode.RightArrow))
{
this.transform.position += Vector3.right * speed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.LeftArrow))
{
this.transform.position += Vector3.left * speed * Time.deltaTime;
}
}
void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "floor") { onGround = true; }
}
void OnCollisionStay2D(Collision2D col)
{
if(col.gameObject.tag == "floor") { onGround = true; }
}
}
Your problem stems from a misunderstanding how the Update method and physics work. If you do this in the update method, it will create an endless loop:
while(jumped)
{
this.rigidbody2D.AddForce(new Vector2(0, jumpVelocity));
if(onGround)
{
jumped = false;
}
}
The thing is that you tell the physics body to add a force. But you keep doing so over and over again. The physics simulation only takes place after the Update method returns, so whatever "onGround" is it will never become true because the forces aren't being applied until after the Update method.
Instead you have to make this check over and over again every time the Update method runs until onGround is true.

Categories