Gameobject moving oddly - unity3d

My gameobjects are moving oddly.
On the enemy, I have this script :
public float speed = 1.0f;
private Transform target;
public void Start()
{
var player = GameObject.FindWithTag("Player");
target = player.transform;
}
void Update()
{
// Move our position a step closer to the target.
float step = speed * Time.deltaTime; // calculate distance to move
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
// Check if the position of the cube and sphere are approximately equal.
if (Vector3.Distance(transform.position, target.position) < 0.001f)
{
// Swap the position of the cylinder.
target.position *= -1.0f;
}
}
After I hit the enemy with a projectile, it starts moving slower. The script behind the projectile is this :
if (coll.gameObject.tag != "Player")
{
Destroy(gameObject);
if ((coll.collider.GetComponent("Damageable") as Damageable) != null)
{
var d = coll.collider.GetComponent<Damageable>();
d.Damage(1);
}
}
I added this script in, as a Damageable component, however, this behavior was there even before this script was active, so I don't think it's related :
public void Damage(int damageAmount)
{
print("Damage : " + Health + ":" + damageAmount);
Health -= damageAmount;
if (Health <= 0)
{
Destroy(gameObject);
}
}
Any recommendations on what is wrong?

coll
I guess this is short for collision, collision happens on 2 rigidbodies (or another static collider, but unrelated to the question), so when the bullet hit the enemy, it also block the enemy's path, even if you destroy it immediately the enemy will still stop moving for 1 frame.
So make the collider on the bullet to be a trigger, a trigger won't block other rigidbodies.
Use OnTriggerEnter2D (or OnTriggerEnter(Collider) for 3D game) to receive touch event.
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag != "Player")
{
Destroy(gameObject);
var d = other.GetComponent<Damageable>();
if(d != null)
d.Damage(1);
}
}

Related

Unity2D Enemy RigidBody Sticking To Player

Video example: https://imgur.com/a/d7MRjVG
Hello, my enemy object is getting stuck to my player object and dragged along. Normally, the enemy has a much lower movement speed than my player does. But when stuck to the player, the enemy essentially inherits my player's movement. I should note that this ONLY happens when the enemy is higher than my player ie has a greater Y position
Player is a rigidbody with Kinematic type
Enemy is a rigidbody with Dynamic type (I wanted this so I can push enemies aside)
Enemy is scripted not to attempt movement if too close to the player as well
The enemy has a pretty basic movement script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AI_Common : MonoBehaviour
{
public float moveSpeed; // Movement speed
public float contactDamageDistance; // offset for contact damage and when to stop walking
public ContactFilter2D movementFilter; // Filter of object types to collide with
Vector2 proposedMovement; // movement to check for collisions before executing
List<RaycastHit2D> castCollisions = new List<RaycastHit2D>(); // Collision list
float myX; // Used for incrementing movement
float myY; // Used for incrementing movement
GameObject player;
Rigidbody2D rBody;
Animator animator;
SpriteRenderer spriteRenderer;
// Start is called before the first frame update
void Start()
{
player = GameObject.FindWithTag("Player");
rBody = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
spriteRenderer = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
}
// Once per physics frame
private void FixedUpdate()
{
animator.SetBool("IsWalking", false);
// Attempt to move toward player if far enough away
if (Vector2.Distance(player.transform.position, rBody.transform.position) < contactDamageDistance)
return;
bool moveSuccess = false;
myX = 0;
myY = 0;
if (player.transform.position.x > rBody.position.x)
{
myX = moveSpeed * Time.fixedDeltaTime;
} else if (player.transform.position.x < rBody.position.x)
{
myX = -moveSpeed * Time.fixedDeltaTime;
}
// Flip X if needed
if(player.transform.position.x - rBody.position.x < -contactDamageDistance)
{
spriteRenderer.flipX = true;
}
else if (player.transform.position.x - rBody.position.x < contactDamageDistance)
{
spriteRenderer.flipX = false;
}
if (player.transform.position.y > rBody.position.y)
{
myY = moveSpeed * Time.fixedDeltaTime;
}
else if (player.transform.position.y < rBody.position.y)
{
myY = -moveSpeed * Time.fixedDeltaTime;
}
print($"MyX:{rBody.transform.position.x}, MyY:{rBody.transform.position.y}, PlayerX{player.transform.position.x}, playerY{player.transform.position.y}");
print($"Distance:{Vector2.Distance(player.transform.position, rBody.transform.position)}");
moveSuccess = TryMove(new Vector2(myX, myY));
// If move failed, try X only
if (!moveSuccess && myX != 0)
{
moveSuccess = TryMove(new Vector2(myX, 0));
}
// If move failed, try Y only
if (!moveSuccess && myY != 0)
{
moveSuccess = TryMove(new Vector2(0, myY));
}
animator.SetBool("IsWalking", moveSuccess);
}
private bool TryMove(Vector2 moveDirection)
{
// Check for collisions
int count = rBody.Cast(
moveDirection, // X and Y values -1 to 1
movementFilter, // Specifies which objects are considered for collision
castCollisions, // List of collisions detected by cast
moveSpeed * Time.fixedDeltaTime + contactDamageDistance); // Length of cast equal to movement plus offset
// If no collisions found, move
if (count == 0)
{
rBody.MovePosition(rBody.position + moveDirection);
return true;
}
return false;
}
}

Unity 2D Platformer Jumping Issue

I'm able to get my sprite to jump using a Axis.RawInput. This input also serves as a parameter to trigger the jumping animation when the RawInput is greater than 0. This issue with this is when you release the key, the sprite instantly falls back down. How can I perform a fixed jump when the key is pressed once or held down and then have the sprite fall at a fixed rate while also having the animations trigger?
This is what I have in my PlayerMover script now.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMover : MonoBehaviour
{
public Animator anim;
public Vector3 velocity = Vector3.zero;
public float runSpeed = 0f;
public float jumpSpeed = 0f;
private SpriteRenderer sp;
public float maxJump = 4f;
private void Awake()
{
sp = GetComponent<SpriteRenderer>();
}
void Update()
{
runSpeed = Input.GetAxisRaw("Horizontal");
jumpSpeed = Input.GetAxisRaw("Vertical");
anim.SetFloat("Jump", jumpSpeed);
anim.SetFloat("Speed", Mathf.Abs(runSpeed));
}
private void FixedUpdate()
{
Move(runSpeed, jumpSpeed*4);
}
void Move(float horizontal, float vertical)
{
if(horizontal > 0 || horizontal > 0 && vertical >0)
{
anim.SetBool("Idle", false);
sp.flipX = false;
}
else if(horizontal < 0 || horizontal <0 && vertical >0)
{
anim.SetBool("Idle", false);
sp.flipX = true;
}
else
anim.SetBool("Idle", true);
velocity.x = horizontal;
velocity.y = vertical;
transform.position += velocity * Time.fixedDeltaTime;
}
}
I've read about using something like
if(Input.GetKeyDown(GetKeyCode("Space"){
rigidbody2D.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
In order to move but it allows additional jumps whenever Space is pressed.
If you want to use something like:
if(Input.GetKeyDown(GetKeyCode("Space"))){
rigidbody2D.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
but need to stop the player from jumping when they're already in the air, just only allow them to do this when they're already on the floor. So:
if(Input.GetKeyDown(GetKeyCode("Space")) && isOnFloor()){
rigidbody2D.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
Where isOnFloor() is some function that checks if your character is on the floor. Depending on your game implementation this could be done several ways, but the most common one is to check if the player character is colliding with anything, and if so, if that collision object is below them. This stackexchange thread gives some code samples for how to achieve this.
In order to use this
if (Input.GetKeyDown(GetKeyCode("Space"))
{
rigidbody2D.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
you need to check if your player is colliding with the ground, so instead you would have a bool like bool isGrounded to check if you are touching the ground, to do this you can do it in the OnCollisionStay() to confirm it is true when you are colliding with the ground. You can use tags to check if the collider you are colliding with is the ground. Then when you jump, you need to say isGrounded = false; then it will not be true untill you land on the ground again
if (Input.GetKeyDown(GetKeyCode("Space") && isGrounded == true)
{
rigidbody2D.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
isGrounded = false;
}

Unity jumping issue, character is not jumping at all

(This is 2D project)
My character need to run left and right and jump on ground. It is my first experience with Unity and it latest available version of it, I manage to make my Character run left and right and flip when it is change direction, but it does not jump at all.
For the Character I am currently using 2 Box Collider 2Ds and Rigidbody 2D. Character has mass equal 1.
For the Ground I am currently using 2 Box Collider 2Ds. Ground is single sprite which is cover bottom part of screen.
Below is the code for jumping and grounded I am currently trying to use.
'''
{
public float maxSpeed = 10f;
public float jumpVelocity = 10f;
private bool isGrounded;
private float move = 0f;
private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics.
private SpriteRenderer sprt_render;
// Start is called before the first frame update
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
isGrounded = true;
}
// Update is called once per frame
void Update()
{
GetInput();
}
private void GetInput()
{
move = Input.GetAxis("Horizontal");
if (transform.position.x > -6.2 && transform.position.x < 5.7)
{
rb2d.velocity = new Vector2(move * maxSpeed, rb2d.velocity.y);
}
else
{
rb2d.velocity = new Vector2(move * maxSpeed * -1, rb2d.velocity.y);
}
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
Debug.Log("Jump");
rb2d.velocity = new Vector2(rb2d.velocity.x, jumpVelocity);
isGrounded = false;
//rb2d.AddForce(Vector2.up * jumpVelocity, ForceMode2D.Impulse);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Ground"))
{
isGrounded = true;
}
}
}
'''
As you can see I try to use two different approach in connection with jump realisation using Velocity and using AddForce result is totally the same it is not jumping even first time. I check thought breakpoint it definitely go trough jumping code but nothing happened.

Can I have two colliders attached to my enemy that do different things and if so how?

Is it possible to have two colliders for one object?
My situation is that I have a CircleCollider2D that causes my enemy to chase the player when it enters. This works well but I want to also have a BoxCollider2D that will switch scene to my scene called "BattleScene" when the player enters.
I want it so that when my player enters the circle collider my enemy will follow him but when the player gets closer and enters the box collider (both attached to the enemy) it will switch scenes to the scene called "BattleScene".
Another alternative I thought of was using a rigid body collision but I don't know how to implement that.
Here is my code
private bool checkContact;
private bool checkTrigger;
public float MoveSpeed;
public Transform target;
public Animator anim;
public Rigidbody2D myRigidBody;
BoxCollider2D boxCollider;
public string levelToLoad;
// Start is called before the first frame update
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();//getting the position of our player
anim = GetComponent<Animator>();
myRigidBody = GetComponent<Rigidbody2D>();
boxCollider = gameObject.GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
if (checkTrigger == true)
{
transform.position = Vector2.MoveTowards(transform.position, target.position, MoveSpeed * Time.deltaTime); //move towrds from your position to the position of the player
if (myRigidBody.position.y < target.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x)) //if it is further away from target in x direction than y direction the animation for moving in y is loaded and vice versa
{
anim.SetFloat("MoveY", 1);
anim.SetFloat("MoveX", 0);
}
if (myRigidBody.position.y > target.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveY", -1);
anim.SetFloat("MoveX", 0);
}
if (myRigidBody.position.x > target.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveX", -1);
anim.SetFloat("MoveY", 0);
}
if (myRigidBody.position.x < target.position.x && Mathf.Abs(target.position.y -myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveX", 1);
anim.SetFloat("MoveY", 0);
}
anim.SetBool("checkTrigger", checkTrigger); //updating if in range
}
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
checkTrigger = true; //setting our check trigger = true so it will follow if in radius
anim.SetBool("checkTrigger", checkTrigger);
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
checkTrigger = false; //setting our check trigger = false so it will not follow if not in radius
anim.SetBool("checkTrigger", checkTrigger);
}
EDIT: THIS PROBLEM HAS BEEN RESOLVED
The best way to handle this is by having an empty GameObject with the other collider attached to it, while making sure both GameObjects have a Rigidbody - the child with IsKinematic ticked. Why? Because this will separate the child GameObject from the parent collision structure. Read more about compound colliders.
You should only have more than one collider in one GameObject if they all make part of the same collision structure. If they have different purposes, use different GameObjects with kinematic Rigidbodies, each handling it's own task.
In your specific scenario, I would have the CircleCollider in the enemy itself and the BoxCollider in a child GameObject with a kinematic Rigidbody. This child GameObject can also contain a script with the sole purpose of checking against the Player and loading the BattleScene.

(Unity2D) Switch movement style on collision

I have a player character in a topdown game that moves in grid-like movements (1 unit at a time), but when it hits a patch of ice (square), I want it to switch to lerp-movement, slide to the edge, and stop.
Currently I have 5 different colliders as children for each patch of ice: the ice collider itself, and 4 slightly distanced colliders, one for each side of the ice. When it hits the ice collider, depending on which direction it was heading in, it should lerp to the distanced collider associated.
Like so (it's hard to see the main collider but it's there):
Here is the code I have been using for the down key (its basically the same for all keys):
else if (Input.GetKeyDown(KeyCode.DownArrow))
{
Vector2 movementDown = new Vector2(0, -1);
RaycastHit2D hitDown = Physics2D.Raycast(transform.position, movementDown, 0.05f);
if (hitDown.collider && hitDown.collider.gameObject.tag == "barrier")
{
Debug.Log("N/A");
}
else if (onIce)
{
player.transform.position = Vector3.Lerp(transform.position, downIce.transform.position, 100 * Time.fixedDeltaTime);
}
else
{
player.transform.position += new Vector3(movementDown.x, movementDown.y, -0.1f);
}
}
EDIT: code that updates bool 'onIce':
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "ice") {
onIce = true;
}
}
void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.tag == "ice")
{
onIce = false;
}
}
Lerp is only getting called once, when you push the button. One way to fix it is by using a coroutine:
IEnumerator Slide() {
var t = 0f;
var start = player.transform.position; //we will change the position every frame
//so for lerp to work, we need to save it here.
var end = downIce.transform.position;
while(t < 1f){
t += Time.deltaTime;
player.transform.position = Vector3.Lerp(start, end , t);
yield return null; //returning null waits until the next frame
}
}
And then you use it like this:
...
else if (onIce)
{
this.StartCoroutine(this.Slide());
}
else
...
I think this is still not exactly what you want in your game because it will lerp to the center of the collider. If that's the case you can easily fix it by changing how it calculates the end variable in the coroutine to make the player only slide along the correct axis.