Unity: Player in front of enemy check not working, Physics.Raycast? - unity3d

This the game scene with two use cases as follows:
The player cannot be seen by the enemy:
The player can be seen by the enemy:
I am using the following code to decide whether the line of vision between the player and the enemy is clear for the enemy to attack, i.e., the enemy should only attack when there is no wall or any other collider between the player and the enemy:
void playerInRangeOfEnemy()
{
// After checking player is in range of the enemy, check for the line of vision between them
Transform Player = FindObjectOfType<Player>().transform;
Vector2 rayDirection = Player.position - transform.position; // --> transform.position is of the enemy
RaycastHit hit;
if(Physics.Raycast(transform.position, rayDirection, out hit, minAttackDistance))
{
Debug.Log("Ray hitting the player"); // Not logging in either of the two cases mentioned above
if (hit.collider.gameObject.CompareTag("Player"))
{
// The following line is also not being logged
Debug.Log("Make the enemy attack the player");
}
}
}
Both the enemy and the player are dynamic Rigidbodies. minAttackDistance is just the length of the raycast I want to cast, is set to 100 for now for testing.
How can I make the condition to be true so that both of the Logs are executed? Any updates to the above code?

You are using the 3d physics not 2d. Please use Physics2D.Raycast instead.

Related

Weapon Pick-Up and shoot

I'm new to Unity and I was wondering if anyone knows how to make the player pick up a weapon and shoot it in Unity 2D. I have already made the sprite for the gun and I've been trying to pick it up with this code:
public GameObject player;
void Update() {
void OnTriggerEnter2D(Collider2D col) {
if (col.tag == "Player") {
gameObject.transform.position = new Vector3(player.transform.position.x + 2, player.transform.position.y, player.transform.position.z);
}
}
}
The gun is set as a trigger and has no rigidbody. All the youtube tutorials show how to pick up an object and then destroys it, but don't show how to "hold" the object.
If you do player.transform.position.x + 2, that sets the gun to the x position of the player + 2. This is not a good way of doing it.
My advice is to do it this way instead.
Copy the gun.
Parent the copy to the camera
Place the gun somewhere on your player.
Disable the gun
In your player script add this line
public GameObject weapon;
Now, in your weapon pickup script, change the player reference to whatever your player script is called instead of a GameObject.
For example, lets assume your player script is called PlayerScript.cs, instead of
public GameObject player;
You should do
public PlayerScript player;
Now, we have a reference to the player script. We can now access the weapon.
Change the OnTrigger to the following:
void OnTriggerEnter2D(Collider2D col) {
if (col.tag == "Player") {
// Set the weapon active
player.weapon.SetActive(true);
// Destroy this pick-up
Destroy(gameObject);
}
}
Also, in your code you have ontriggerenter inside the update method. That is not how it works. Make sure the method is outside of the update method.
Now when you touch the gun on the floor. It should now enable the gun in the players hand and destroy the one on the floor.
In the editor, drag the players gun to the weapon field in the player script.
Also, drag the player to the player field of the gun on the ground.
Hope this helps, feel free to ask more questions if this doesn't work. Keep us updated. :)

How to create a Raycast that will allow a turret to see if a player is behind a wall?

so, basically, I have created a pretty simple turret script that basically just smoothly aims at the player, so long as the player is within a certain amount of range. The problem I am having, is that the Raycast I wrote that actually checks if the 'bullet' (which is nothing - it's just a raycast), would hit the target. This means that even if the player hides behind a wall, the turret can still shoot him.
My current raycast script allows the raycast to go straight through the wall, and since I am new to Unity, I have no idea how to make it check if the first object it hits is the player, so that it cannot go through walls.
Here is my current raycast script:
void Shoot()
{
//I think the problem is here - I want the raycast to return false if it hits a wall - which has the layer "ground", and true if it hits the player. Problem is, I need to make the turret return to resting position when the player is behind a wall.
//To do this, I can just set inRange = true; But I need to be able to determine when the player is behind a wall.
LayerMask layerMask = LayerMask.GetMask("Player");
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
{
//This determines how much damage will the player take.
int damage = Random.Range(1, 5);
hit.collider.gameObject.GetComponent<playerMovement>().Shot(damage);
//I personally THINK this means that it only triggers collisions with the player, which is why it is not working.
// The player has layer "Player", and tag "Player", so if anyone who wants to help can figure out how to make it stop when it hits anything - and then only return true if it hit the player (meaning the player is not behind walls).
}
}
If you want to check if there is anything between the player and the Raycast, then simply remove the Layermask
Change this:
LayerMask layerMask = LayerMask.GetMask("Player");
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
To this:
Ray ray = new Ray(transform.position, transform.TransformDirection(Vector3.forward));
if (Physics.Raycast(ray, out RaycastHit hit) {..}
You want to
remove the check for the layer in order to hit everything with the raycast
then you can use TryGetComponent to check whether the hit object has such component attached or not
and in general instead of
transform.TransformDirection(Vector3.forward)
simply use transform.forward ;)
So something like
void Shoot()
{
if (Physics.Raycast(transform.position, transform.forward, out var hit))
{
// Without the need for any tag or layer check
// Simply check if the object you hit has a playerMovement component
// GetComponent was improved a lot lately and now uses hashes
// it's not that performance intense anymore and almost as fast as CompareTag
if(hit.gameObject.TryGetComponent<playerMovement>(out var movement)
{
int damage = Random.Range(1, 5);
movement.Shot(damage);
}
}
}
All you'd need to do is cast from the turret to the player and detect what the raycast has hit. Your current code is setting a mask to only be on the player, so it will never hit a wall. You can change your code to something like this:
private LayerMask layerMask = (1 << LayerMask.NameToLayer("Player") | (1 << LayerMask.NameToLayer("ground")));
void Update () {
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out RaycastHit hit, Mathf.Infinity, layerMask))
{
if(hit.collider.gameObject.layer == LayerMask.NameToLayer("Player"))
{
// you can shoot as you see the player
}
else
{
// you hit the ground - player is behind a wall
}
}
}

There is an invisible force-field around my player that pushes the enemy back

My enemy just can't move past a point and it tries to push the player. If my player moves the enemy just flies off the grid.
There is only a Rigidbody component added to my enemy and a character controller component added to my player. There is not a single other Rigidbody or collider component.
This is the code that does my enemy movement:
if (direction.magnitude > 0.35 && zombie.GetCurrentAnimatorClipInfo(0)[0].clip.name != "Zombie Attack")
{
pos = this.transform.position;
pos.z = pos.z + speed;
this.transform.GetComponent<Rigidbody>().MovePosition(pos);
zombie.SetBool("isWalking", true);
zombie.SetBool("isAttacking", false);
}
I think there is something in the Player GameObject that pushes my enemy but I removed every collider and Rigidbody.
Your problem is that: Transform.Translate is moving the transform component while ignoring physics.
If you move them via. Rigidbody.MovePosition they should be able to collide.

Two Gameobjects collides although they are not on the same place

I have a Gameobject called ball and another called floor. As can be seen in the code, when the ball hits the floor, a printout shall be made.
void OnCollisionEnter2D(Collision2D collision)
{
bg = GameObject.Find(bg_name);
if (collision.gameObject.tag == "floor")
{
//bg.GetComponent<ControlGame>().deleteBall();
//bg.GetComponent<ControlGame>().recreateBall();
print("Ball hit floor");
}
if (collision.gameObject.tag == "rope")
{
bg.GetComponent<ControlGame>().ToggleIsShot(false);
}
}
The problem is that I have moved the floor now, such that the ball and the floor has different depth. Still, the printout is made although the two Gameobjects are not touching. See the screenshots below:
The print is in the lower left corner. Why is that?
According to the comment below, the z-value does not have an effect. But I have also a collision detector for the rope, which is a Bezier-curve with an EdgeCollider2D attached to it. When the rope passes the floor, nothing is triggered in onCollisionEnter2D.
void OnCollisionEnter2D(Collision2D collision)
{
bg = GameObject.Find(bg_name);
if (collision.gameObject.tag == "floor")
{
print("floor collision");
bg.GetComponent<ControlGame>().ShootBall();
}
}
Well, these are Collider2D.
You only changed the Z (depth) position of the floor which basically is completely ignored in a 2D game / by 2D physics.
As you can also see in the GameView for the Physics 2D the objects are still aligned and colliding.
Why isn't it also working for collision between rope and floor?
Please checkout the Collision Action Matrix for which collisions/trigger can be detected.
&rightarrow; At least one of the objects has to be a Rigidbody2D.
If the rope is e.g. moved by script then it has to be a Kinematic &rightarrow; in this case also the floor would need to be a Rigidbody2D and not Kinematic.
The reason I guess it worked just fine for the ball is because it already is a non-kinematic Rigidbody2D so able to detect collisions with static colliders.

Recommended Approach to multiple colliders

I am have some difficulty with what is probably a very silly thing. I have an enemy gameobject that depending on where it is hit (collision) - either it, or the play dies. I think the simplest way to describe this is by using the classic Super Mario Bros. game as an example.
As you all know, if the player runs into the enemy - the player will lose - UNLESS he jumps on top of the enemy's head, in which case the enemy should die.
My initial idea was to create two colliders on the gameobject:
Blue border represents a BoxCollider2D - that if collided with - will cause player to lose (notice it is slightly lower from the top)
Green border represents a BoxCollider2D on a child gameobject - that if collided with - will cause the gameobject to die.
The following is a simplified version of the code I used:
// Collider #1
public void OnCollisionEnter2D(Collision2D collision)
{
// Trigger 'Game-Over' logic
}
// Collider #2
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
Destroy(this.gameObject);
}
}
This kind-of works, however momentarily after colliding with Collider #1, Collider #2 is also trigger - and while the enemy is destroyed, the player also loses.
I have been playing with the RigidBody2D values to prevent the player from entering the 2nd collider when hitting the enemy from the top - but apparently with that force / speed, the colliders may be slightly inaccurate (or maybe I'm just doing it wrong?).
I have looked into RayCasts but this seems too complex for something that me appears rather trivial (casting rays on all four sides and four vertices of the player - assuming that the player has a box collider).
What I have resorted to 'for the moment' is a a single collider with a simple piece of code that I am unhappy with, and doesn't always work:
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
float yVelocity = collision.gameObject.transform.rigidbody2D.velocity.y;
if (yVelocity < 0)
{
Debug.Log("Enemy will lose..." + yVelocity);
Destroy(this.gameObject);
}
else
{
// Trigger 'Game-Over' logic
}
}
}
I'm currently working in 2D mode, but solutions for 3D mode (while maybe more complicated than necessary for my question) will also be considered.
Thanks guys.
as a game developer you always have many ways to solve a problem or make a gameplay.
first of all i have to say you should make a polygon collider 2d fo you objects and chracters. just colliding pictures is not very good as i see you used box cilliders in your game.
a good choice can be that you attach and empty object ot you player and set its position under foots of you player and check of enemy hit that enemy dies else if enemy hit main character object, player dies.
another choice can be when o objects collide check y position of 2 objects. of player was higher he kiils, else enemy kills the player.
if you think more you will find more answers.
you have to examin diffrent ways and find most efficient.
I think it will be easy to disable the other collider when one is triggered. You can easily enable/disable colliders with collider.enabled = true; or collider.enabled = false;
// Collider #1
public void OnCollisionEnter2D(Collision2D collision)
{
// Trigger 'Game-Over' logic
// collider2.enabled = false;
}
// Collider #2
public void OnCollisionEnter2D(Collision2D collision)
{
// collider1.enabled = false;
if (collision.gameObject.tag == "Player")
{
Destroy(this.gameObject);
}
}
This way it will be pretty lightweight and easy to implement.
One way of implementing what you want is to put each collider in its child own game object and use the IsTouching() method.
void OnTriggerEnter2D(Collider2D other){
if(GameObject.Find("Top Trigger").GetComponent<BoxCollider2D>().IsTouching(other)){
Destroy(transform.gameObject)
}
if(GameObject.Find("Bottom Trigger").GetComponent<BoxCollider2D>().IsTouching(other)){
Destroy(other.gameObject)
}
}