GameObject Follow cursor yet also follows enemies? - unity3d

I'm making a simple character that follows the player's cursor. What I also want is for when the game object "enemy" appears the character then goes to that location to alert the player. Once the enemy is gone the character continues to follow the cursor like normal. Is there a reason why my script won't work. How else can I paraphrase it?
public class FollowCursor : MonoBehaviour
{
void Update ()
{
//transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
if (gameObject.FindWithTag == "Enemy")
{
GameObject.FindWithTag("Enemy").transform.position
}
if (gameObject.FindWithTag != "Enemy")
{
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}

You are not using FindWithTag correctly, as it is a method that takes a string as parameter you need to use it like this:
GameObject.FindwithTag("Something") as stated in the Unity scripting API
Now to apply this to your code you would need to do the following to set your players position based on wether or not an enemy is found (assuming this script is on your actual player object):
if(GameObject.FindWithTag("Enemy"))
{
//If an enemy is found with the tag "Enemy", set the position of the object this script is attatched to to be the same as that of the found GameObject.
transform.position = GameObject.FindWithTag("Enemy").transform.position;
}
else
{
//when no enemy with the tag "Enemy" is found, set this GameObject its position to the the same as that of the cursor
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
However this code will just snap your player instantly to the position of the found Enemy. If this is not the desired behaviour you could use a function like Vector3.MoveTowards instead to make the player move to it gradually.
This code also has room for optimisation as searching for a GameObject every update frame is not the ideal solution. But for now it should work.

I'm going to code coding all the function for you, I'm not pretty sure about the beavihour of your code, I understand a gameobject will be attached to the mouse position, so not really following....
Vector3 targetPosition;
public float step = 0.01f;
void Update()
{
//if there is any enemy "near"/close
//targetPosition = enemy.position;
//else
//targetPosition = MouseInput;
transform.position = Vector3.MoveTowards(transform.position, targetPosition , step);
}
For the f you can use a SphereCast and from the enemies returned get the closest one.

Related

2D Object Not Detecting When Hit by Raycast

I'm creating a top down 2D game, where the player has to break down trees. I made it so the player casts a ray toward the mouse, and when the ray hits a tree, it should lower the tree's health. I don't get any errors when I run the game or click, but it seems like the tree isn't detecting the hits.
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.Raycast(playerRb.transform.position, mousePosition - playerRb.transform.position, 2.0f);
if (hit.collider != null)
{
if (hit.collider == GameObject.FindWithTag("Tree"))
{
hit.collider.GetComponent<TreeScript>().treeHealth--;
}
}
}
}
Still pretty new to coding and I'm teaching myself, so please make your answer easy to understand to help me learn.
Input.mousePosition is equal to the pixel your mouse is on. This is very different than the location your mouse is pointing at in the scene. To explain further, Input.mousePosition is where the mouse is. Think about it. If the camera was facing up, the mouse positon would be the same, but where they are clicking is different.
Instead of using Input.mousePosition, You should pass this into a function called Ray Camera.ScreenPointToRay();
You just input the mouse position and then use this new ray to do the raycast.
ANOTHER EXTREMELY IMPORTANT THING 1: Do not use Camera.main in Update(), as it uses a GetComponet call, which can cause perormance decreases. Store a reference of it in your script and use that.
Extremely important thing 2: I notice you are using GetComponent to change the tree's health. This is fine, but do not use GetComponent if you don't have to.
Like this:
Camera cam;
void Start()
{
cam = Camera.main; //it is fine to use this in start,
//because it is only being called once.
}
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray);
...
}
}
You need to convert your mouse position from screen point to world point with Z value same as the other 2D objects.
Vector3 Worldpos=Camera.main.ScreenToWorldPoint(mousePos);
Also use a Debug.DrawRay to check the Raycast
Debug.DrawRay(ray.origin, ray.direction*10000f,Color.red);
Source

Raycast2D hits only one side of Collider

I want to make sure that various objects moving at high speed cannot pass through walls or other objects. My thought process was to check via Raycast if a collision has occurred between two moments of movement.
So the script should remember the previous position and check via Raycast for collisions between previous and current position.
If a collision has occurred, the object should be positioned at the meeting point and moved slightly in the direction of the previous position.
My problem is that works outside the map not inside. If I go from inside to outside, I can go through the walls. From outside to inside not.
Obviously I have misunderstood something regarding the application with raycasts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObsticalControll : MonoBehaviour
{
private Vector3 positionBefore;
public LayerMask collisionLayer = 9;
private Vector3 lastHit = new Vector3(0, 0, -20);
// Start is called before the first frame update
void Start()
{
positionBefore = transform.position;
}
private void OnDrawGizmos()
{
Gizmos.DrawCube(lastHit, new Vector3(.2f,.2f,.2f));
}
// Update is called once per frame
void Update()
{
Vector3 curruentMovement = transform.position;
Vector2 dVector = (Vector2)transform.position - (Vector2)positionBefore;
float distance = Vector2.Distance((Vector2)positionBefore, (Vector2)curruentMovement);
RaycastHit2D[] hits = Physics2D.RaycastAll((Vector2)positionBefore, dVector, distance, collisionLayer);
if(hits.Length > 0)
{
Debug.Log(hits.Length);
for (int i = hits.Length -1 ; i >= 0 ; i--)
{
RaycastHit2D hit = hits[i];
if (hit.collider != null)
{
Debug.Log("hit");
lastHit.x = hit.point.x;
lastHit.y = hit.point.y;
Vector3 resetPos = new Vector3(hit.point.x, hit.point.y, transform.position.z) + positionBefore.normalized * 0.1f;
transform.position = new Vector3(resetPos.x, resetPos.y, transform.position.z);
}
}
}
positionBefore = transform.position;
}
}
Theres a better way to deal with this that unity has built in.
Assuming the object thats moving at a high speed has a RigidBody(2d in your case) you can set its Collision Detection to Continuous instead of Discrete.
This will help collisions with high speed collision, assuming that its moving at high speed and the wall is not moving.
If for some reason you cannot apply this to your scenario, Ill try to help with the raycast solution.
However, I am still wondering about the collision behavior of my raycast script. That would be surely interesting, if you want to calculate shots or similar via raycast
Alright, so your initial idea was to check if a collision had occurred, By checking its current position and its previous position. And checking if anything is between them, that means a collision has occurred. And you would teleport it back to where it was suppose to have hit.
A better way todo this would be to check where the GameObject would be in the next frame, by raycasting ahead of it, by the distance that it will travel. If it does hit something that means that within the next frame, it would have collided with it. So you could stop it at the collision hit point, and get what it has hit. (This means you wouldn't have to teleport it back, So there wouldn't be a frame where it goes through then goes back)
Almost the same idea but slightly less complicated.
Problem would be that if another object were to appear between them within the next frame aswell, it could not account for that. Which is where the rigidbody.movePosition shines, And with OnCollisionEnter you can detect when and what it collided with correctly. Aswell as without the need to teleport it back

How to create a projectile Script in Unity

so recently my friends and I are trying to make a game for fun. Currently, I hit a wall and not sure how to do this. I am trying to create a simple base script where I have the character move and attack with right click. If it hits the ground, it will move there and and if in range of a target, it will send a projectile. So the game is creating the projectile but its not actually moving. Can anyone tell me what I should probably do. At first, I thought to just make it all one script but now I am thinking it be best to make another script for the projectile.
void Update()
{
if (Input.GetMouseButtonDown(1))
{
RaycastHit hit;
if(Physics.Raycast (Camera.main.ScreenPointToRay(Input.mousePosition), out hit) && hit.transform.tag == "Minion")
{
if(Vector3.Distance(this.transform.position, hit.point) <= atkRange)
{
GameObject proj = Instantiate(bullet) as GameObject;
proj.transform.position = Vector3.MoveTowards(this.transform.position, target, atkSpd);
}
}
else if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, speed))
{
agent.destination = hit.point;
}
}
}
So this is what I originally had. I am pretty sure I did something wrong here. Also I am not sure if I should have another script for the projectile itself or if it is not necessary. Thank you for any help or tips on what to do.
For starters, I'd advize using a Rigidbody component and letting physics handle movement but if you'd like to use Vector3.MoveTowards, it'll be a bit of work:
Vector3.MoveTowards is something that needs to be called every frame. I'm guessing bullet is your prefab so you'll want to make a new script for the movement and place it on that prefab:
public class MoveToTarget : MonoBehaviour
{
private float _speed;
private Vector3 _target;
public void StartMovingTowards(Vector3 target, float speed)
{
_target = target;
_speed = speed;
}
public void FixedUpdate()
{
// Speed will be 0 before StartMovingTowards is called so this will do nothing
transform.position = Vector3.MoveTowards(transform.postion, _target, _speed);
}
}
After you've attached this to your prefab, make sure you grab a reference and get it started when you instantiate a copy of your prefab:
GameObject proj = Instantiate(bullet) as GameObject;
var movement = proj.GetComponent<MoveToTarget>();
movement.StartMovingTowards(target, atkSpd);
If you instead go the physics route, add a Rigidbody component to your bullet prefab, and get a reference to that instead of making the MoveToTarget script:
GameObject proj = Instantiate(bullet) as GameObject;
var body = proj.GetComponent<Rigidbody>();
Then you can just apply a force and let physics take over:
body.AddForce(target - transform.position, ForceMode.Impulse);
Don't set the position like you currently are
proj.transform.position = Vector3.MoveTowards(this.transform.position, target, atkSpd);
Instead, add either a characterController or a rigidbody and use rb.addVelocity.

Prevent Collision Forces in Unity Physics in Collision Callback

How do I prevent a collision from applying forces in Unity? I am using 2D physics and want an arrow to stick into a crate. I can easily remove the rigid body and collider in the collision callback, but it seems that a frame of collision force is still applied to the arrow, causing slight jumps in position and rotation. Settings isKinematic on the rigid bodies in the collision callback also appears to not prevent this one frame of force being applied.
I am hoping to tell Unity to not apply physics for the collision.
Using kinematic for the life time of the arrow is not an option because the arrow needs to fly realistically until it hits something.
Here is the code for the crate object that handles the collision:
protected virtual void HandleCollision(ArrowScript arrow, Collision2D coll)
{
StickArrow(arrow, coll);
if (DestroyAfterSeconds >= 0.0f)
{
Destroy(arrow.gameObject, DestroyAfterSeconds);
}
}
private void OnCollisionEnter2D(Collision2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
HandleCollision(script, coll);
}
}
private bool StickArrow(ArrowScript arrow, Collision2D coll)
{
Vector2 surfaceNormal = coll.contacts[0].normal;
float surfaceAngle = Mathf.Atan2(surfaceNormal.y, surfaceNormal.x);
float arrowAngle = Mathf.PI + (arrow.transform.eulerAngles.z * Mathf.Deg2Rad);
float angleDifference = Mathf.Abs(BowAndArrowUtilities.DifferenceBetweenAngles(surfaceAngle, arrowAngle));
float penetration = arrow.PercentPenetration * PenetrationPercentageModifier * (1.0f - angleDifference);
if (penetration <= MinimumPenetrationPercentage)
{
arrow.PercentPenetration = 0.0f;
return false;
}
// Make the arrow a child of the thing it's stuck to
arrow.transform.parent = transform;
arrow.gameObject.transform.Translate(new Vector3(-penetration * arrow.Length, 0.0f, 0.0f));
SpriteRenderer thisSpriteRenderer = GetComponent<SpriteRenderer>();
if (thisSpriteRenderer != null)
{
arrow.GetComponent<SpriteRenderer>().sortingLayerID = thisSpriteRenderer.sortingLayerID;
arrow.GetComponent<SpriteRenderer>().sortingOrder = Mathf.Max(0, thisSpriteRenderer.sortingOrder - 1);
}
BowAndArrowUtilities.PlayRandomSound(arrow.CollisionAudioClips, penetration * 5.0f);
// destroy physics objects from the arrow (rigid bodies, colliders, etc.). This unfortunately doesn't prevent this frame from apply force (rotation, position) to the arrow.
arrow.DestroyPhysicsObjects();
return true;
}
Unity version is 5.3.4.
I ended up making the arrow head a trigger. Inside of OnTriggerEnter2D, I then perform a circle cast in the direction the arrow is pointing with a width of the arrow head sprite. Triggers do not get affected by Unity physics calculations.
private void OnTriggerEnter2D(Collider2D coll)
{
ArrowScript script = coll.gameObject.GetComponent<ArrowScript>();
if (script != null)
{
Vector2 dir = -script.ArrowHead.transform.right;
// ray cast with the arrow size y value (thickness of arrow)
RaycastHit2D[] hits = Physics2D.CircleCastAll(script.ArrowHead.transform.position, script.Size.y, dir);
foreach (RaycastHit2D hit in hits)
{
// collider2d is a member variable assigned in Start that is the Collider2D for this object
if (hit.collider == collider2d)
{
HandleCollision(script, hit.normal);
break;
}
}
}
}
Your problem is that OnCollisionEnter and OnTriggerEnter are called after all the collisions are resolved.
The simplest way to solve this without affecting anything would be to change the weight of the box, arrow, or both.
Set the weight of the crate to a high value, and the weight of the arrow to a low value.
Another way is to use trigger colliders, as you have done. However trigger colliders have problematic side-effects. For example, it doesn't call OnCollisionEnter or OnTriggerEnter on the crate. You will have to do all the logic inside the arrow script, which is not much of a problem.
There are a lot of other ugly hacks however. You could set the velocity of the box to 0 after impact, but it would freeze the crate if you hit it as it was moving. You could use the collision information to cancel the force applied to the crate to solve the collision. You could save the last velocity of the crate every frame, and reapply it to the rigid body during the OnCollision call.
I wouldn't suggest any of these, but they are possible.

Firing on both side Unity 2D

I am trying to make a 2D platformer but my player is not firing on both side I don't know what is wrong with my script.
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour
{
public float bulletSpeed;
public GameObject bullet;
public Transform bulletX;
GameObject clone;
void Update ()
{
if (Input.GetKeyUp ("space"))
{
clone = Instantiate(bullet,new Vector3(bulletX.position.x,bulletX.position.y+0.1f,0f),Quaternion.identity) as GameObject;
if (GameObject.Find ("Player").GetComponent<Player> ().left == true)
bulletSpeed = -30f;
else
bulletSpeed = 30f;
}
bullet.rigidbody2D.velocity = new Vector2(bulletSpeed * 0.5f, 0f);
Destroy (clone, 1f);
}
}
I tried to increase velocity inside the if condition but bullet was moving faster than I needed.
I think your question was very hard to understand what you want to accomplish, but I can see some errors in your code that will render the "clone" you are creating, useless.
The update loop is continuously executing, and you have placed the destroy outside your "Press space" code block. Unity tries to destroy it every frame. Place it inside the space.
I feel it should look more like this:
if (Input.GetKeyUp ("space"))
{
clone = Instantiate(bullet,new Vector3(bulletX.position.x,bulletX.position.y+0.1f,0f),Quaternion.identity) as GameObject;
if (GameObject.Find ("Player").GetComponent<Player> ().left == true)
bulletSpeed = -30f;
else
bulletSpeed = 30f;
bullet.rigidbody2D.velocity = new Vector2(bulletSpeed * 0.5f, 0f);
Destroy (clone, 1f);
}
This might not answer your question, but could you specify more what behaviour you are after? And what object is this script running on? (It is called Bullet with a field referance to another bullet ?)
Debug.Log your bulletSpeed * 0.5f
If I am correct. Even if your Player is Looking at the other way. It is returning
an ABS number meaning (bulletSpeed = -30f & 0.f is != -44954 something but == 44954).
Alternatively you can use AddForce instead of Velocity. It is easier to control.
QUESTION. Why not use Instantiate Vector2?