Unity finding Transform object in scene - c#

I am new at game dev, and I have question, I have my enemies prefabs, and enemy script, contains
public Transform player;
So Instead of every time putting my player into that 'slot', I want to make, script will be finding my player, I tried
private Transform player = GameObject.Find("player")
but it shows error
Here is the full script
public class Enemies : MonoBehaviour
{
public Transform player = GameObject.Find("Player");
private Rigidbody2D rb;
private Vector2 movement;
public float speed = 5f;
public int health;
// Start is called before the first frame update
void Start()
{
rb = this.GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Vector2 direction = player.position - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
rb.rotation = angle;
direction.Normalize();
movement = direction;
}
private void FixedUpdate()
{
Move(movement);
}
void Move(Vector2 direction)
{
rb.MovePosition((Vector2)transform.position + (direction * speed * Time.deltaTime));
}
private void OnMouseDown()
{
health = health - 1;
if(health <= 0)
{
Destroy(gameObject);
}
}
}

First of all you can't use Find in a static context. (Which is probably the error you are referring to.)
It goes a bit deeper into how c# works but in simple words: The class fields are all initialized even before the constructor is executed and thus at a moment when there still is no instance.
Secondly: GameObject.Find returns a GameObject not a Transform.
So if anything it would probably rather be
// Best would still be to drag this in if possible
[SerializeField] private Transform player;
void Start()
{
if(!player) player = GameObject.Find("Player").transform;
rb = this.GetComponent<Rigidbody2D>();
}
In general I always recommend to not use Find at all if anyhow possible. It is basically just a more expensive way of using some static or manager/provider based code

Related

Enemy follow player when player is not looking. OnBecameInvisible not working

So I want to create a weeping angels effect on my enemies in my game. So when the player can see the enemy, they don't move and when the player can't see them, they move closer to the player. This is my code thats attached to the enemy and its not working. Would really appreciate some help!
using System.Collections.Generic;
using UnityEngine;
public class WeepingAngel : MonoBehaviour
{
public GameObject Player;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
void OnBecameInvisible()
{
if (Player)
{
transform.position = Player.transform.position - Player.transform.forward;
Vector3 lookPos = Player.transform.position - transform.position;
lookPos.y = 0;
transform.rotation = Quaternion.LookRotation(lookPos);
}
}
}
I'm gonna assume that the OnBecameInvisible doesn't get called in situations where you think it should. The reason why that may happen is because it will react to ANY camera. That includes even the scene view.
Alternative solution
Instead of OnBecameInvisible you can use the method available in this wiki article:
http://wiki.unity3d.com/index.php?title=IsVisibleFrom
Modified version of your code
using UnityEngine;
public class WeepingAngel : MonoBehaviour
{
public GameObject player;
private new Camera camera;
private new Renderer renderer;
private void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
camera = Camera.main;
renderer = GetComponent<Renderer>();
}
private void Update()
{
bool isVisible = GeometryUtility.TestPlanesAABB(
GeometryUtility.CalculateFrustumPlanes(camera),
renderer.bounds);
if (!isVisible)
TryMovingTowardsPlayer();
}
private void TryMovingTowardsPlayer()
{
if (player == null)
return;
transform.position = player.transform.position - player.transform.forward;
Vector3 lookPos = player.transform.position - transform.position;
lookPos.y = 0;
transform.rotation = Quaternion.LookRotation(lookPos);
}
}

Object not touching ground when moving platform is falling in Unity

I'm using Unity to make my character jump from a moving platform where it goes up & down infinitely. The problem I'm facing is when the moving platform goes up, the jump is working perfectly but when the platform is going down, my character can't jump most often & I can see the platform is "vibrating" a bit which is weird.
Here are my codes:
Moving Platform Script [NB - Rigidbody2D is set to Kinematic]
public class Moveground : MonoBehaviour
{
[SerializeField] private Transform posTop, posBot;
private float maxTop = -0.5f;
private float maxBot = -5.0f;
[SerializeField] private float speed;
[SerializeField] private Transform startPos;
private Vector2 nextPos;
private void Start()
{
nextPos = startPos.position;
}
private void FixedUpdate()
{
if (transform.position == posTop.position)
{
nextPos = posBot.position;
}
if (transform.position == posBot.position)
{
nextPos = posTop.position;
}
transform.position = Vector2.MoveTowards(transform.position, nextPos, speed*Time.deltaTime);
}
}
PlayerController.cs (Only Jump part)
[SerializeField] private LayerMask ground;
private Collider2D coll;
private void Start()
{
coll = GetComponent<Collider2D>();
}
private void Update()
{
InputManager();
}
private void InputManager()
{
if (Input.GetButtonDown("Jump") && coll.IsTouchingLayers(ground)) // Moving Platform's layer is also "ground"
{
Jump();
}
}
private void Jump() {
rb.velocity = new Vector2(rb.velocity.x, jumpforce); // jumpforce is a float number
}
How can I resolve this issue? I'm new to Unity.
Instead of "IsTouchingLayers" try something like this in the PlayerController class:
public bool IsGrounded()
{
return Physics2D.Raycast(transform.position, Vector3.down, 0.1f, ground);
}
and play around with the distance argument, which is the 0.1f one.
If your player transform is not at the bottom of the player, you can also put in something like this instead of 0.1f:
coll.bounds.extents.y + 0.1f

Unity how to "power bounce" one object from another relative to it's angle?

I'm working on a simple 3D game where some balls (fixed Z position) fall along a path (using gravity and physics material) to a small flat platform and "power bounce" off this platform. The player can rotate this platform so I want to recreate a realistic bounce direction according to the platform's angle.
I'm new to coding but so far I've figured the relationship between the vector of the ball as it comes into collision with the platform and the platform's normal, which should be a perpendicular line from the surface and that can be used to reflect the ball's vector to the other direction.
I already used OnCollisionEnter and if statement to detect whether it's the platform you are colliding with, but I don't understand where to indicate the normal of the surface and how to access it. Should it be as a public class in the other object or can it be detected from the ball game object?
I tried some examples from this and other websites and got this far:
public class OnCollision : MonoBehaviour
{
public float speed = 25f;
public Rigidbody rb;
private Rigidbody rigid;
private void Start()
{
rigid = transform.GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.tag == "BouncePad") {
rb.velocity = transform.up * speed;
}
}
}
Now it bounces off vertically, so I'm guessing I should change the code where the transform.up * speed part is.
Could anyone guide me, please?
Much appreciated.
If you are already using Physics material, look into the Bounciness property. A value of 0 means no bounce, a value of 1 will lead to no loss of energy. The angle of the bounce will be calculated for you. Make sure you drag the physics material onto each object-- both the ball and the wall's material will have an effect.
Finally somebody gave me a hand and came to this solution:
public class Bounce : MonoBehaviour
{
public Rigidbody rb;
public float str = 0.21f;
public float str2 = 0.15f;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "BouncePad")
{
rb.AddForce(rb.velocity * str, ForceMode.Impulse);
}
if (col.gameObject.tag == "BouncePad2")
{
rb.AddForce(rb.velocity * str2, ForceMode.Impulse);
}
}
// Update is called once per frame
void Update()
{
}
}
public class BouncTest : MonoBehaviour
{
[SerializeField] private float hight = 3;
[SerializeField] private int times = 5;
[SerializeField] private float speed = 8;
private Vector3 _startPos;
private bool _checkUP;
private int _countTimes;
private float _hightbuf;
[HideInInspector]
public bool _bounceEnd;
private void Awake()
{
_startPos = transform.position;
}
public void TurnOnBounceEffect()
{
_bounceEnd = true;
_checkUP = false;
_hightbuf = hight;
_countTimes = 0;
}
private void FixedUpdate()
{
BounceEffect();
}
private void BounceEffect()
{
if (_bounceEnd)
{
if (!_checkUP)
{
if (transform.position.y <= (_startPos.y + _hightbuf))
transform.position = Vector2.MoveTowards(transform.position, new Vector2(_startPos.x, transform.position.y) + (Vector2.up * _hightbuf), speed * Time.fixedDeltaTime);
else
{
_checkUP = true;
}
}
else if (times != _countTimes)
{
if (transform.position.y > _startPos.y)
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
else
{
_countTimes++;
_checkUP = false;
_hightbuf /= 2;
}
}
else
{
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
if (transform.position.y <= _startPos.y)
{
_bounceEnd = false;
}
}
}
}
}

How do I stop AI movement with OnTriggerEnter2D in Unity?

I'm creating a basic AI script for my enemies in Unity and I have most of it working the way I want it to. The way I have my enemies set up they contain 2 colliders, a polygon collider that destroys the player when touched, and an empty game object that's a child of the enemy that is a circle collider that acts as a trigger. There's a game object that's tagged Straight Road and when the circle collider comes in contact with it, it should run a function called StopMovement(); that sets the enemies movement to 0. I used to Debug.Log(); to check to see if the collider recognizes that it's touching Straight Road and it doesn't. This is my code below. I'm hoping someone has a suggestion.
public class DogAI : GenericController {
public Transform target;
public float chaseRange;
public float maxDistance;
private Vector3 targetDirection;
private float targetDistance;
// Use this for initialization
void Start()
{
base.Start();
}
// Update is called once per frame
void Update()
{
base.Update();
if (target.transform != null)
{
targetDirection = target.transform.position - transform.position;
targetDirection = targetDirection.normalized;
targetDistance = Vector3.Distance(target.position, transform.position);
if (targetDistance <= chaseRange)
{
SetMovement(targetDirection);
}
Vector3 enemyScreenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (targetDistance > maxDistance)
{
Destroy(gameObject);
}
}
}
void StopMovement()
{
SetMovement(new Vector2(0,0));
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.CompareTag("Straight Road"))
{
Debug.Log("Stop! There's a road!");//This never shows up in the log?
StopMovement();
}
}
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Player"))
{
DestroyObject(other.gameObject);
}
}
Generic Controller script below containing the SetMovement() function.
public abstract class GenericController : MonoBehaviour
{
public float movementSpeed = 20;
float animationSpeed = 1;
protected Rigidbody2D rigidbody;
protected Animator animator;
Vector2 movementVector;
float currentSpeed;
protected bool needAnimator = true;
// Use this for initialization
protected void Start()
{
rigidbody = GetComponent<Rigidbody2D>();
if (needAnimator)
{
animator = GetComponent<Animator>();
animator.speed = animationSpeed;
}
}
protected void FixedUpdate()
{
rigidbody.velocity = movementVector;
currentSpeed = rigidbody.velocity.magnitude;
if (needAnimator)
animator.SetFloat("Speed", currentSpeed);
}
public void SetMovement(Vector2 input)
{
movementVector = input * movementSpeed;
}
public void SetMovement(int x, int y)
{
SetMovement(new Vector2(x, y));
}
From the documentation:
MonoBehaviour.OnTriggerEnter2D(Collider2D)
Sent when another object enters a trigger collider attached to this object (2D physics only).
The keyword here is enters. In other words, a trigger is for when something goes inside the area of the collider, like a player entering a region of the map, where the region is a trigger collider. If you want something to happen when a collider collides with the road, i.e. when your CircleCollider comes in contact with the road, then you want the collider to not be a trigger, and you want the functionality to be inside OnCollisionEnter2D.

Trying to launch a projectile towards a gameobject, doesn't move!=

I'm making a 2D Tower Defense game and want my towers to launch a prefab at minions. However it currently only spawns my desired prefab, but doesn't move it.
My two scripts:
public class Attacker : MonoBehaviour {
// Public variables
public GameObject ammoPrefab;
public float reloadTime;
public float projectileSpeed;
// Private variables
private Transform target;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider co){
if (co.gameObject.tag == "Enemy" || co.gameObject.tag == "BlockTower") {
Debug.Log("Enemy tag detected");
if(this.gameObject.tag == "Enemy" && co.gameObject.tag != "Enemy"){
Debug.Log("This is an Enemy");
// Insert for Enemey to attack Block Towers.
}
if(this.gameObject.tag == "Tower" && co.gameObject.tag != "BlockTower"){
Debug.Log("This is a Tower");
Tower Tower = GetComponent<Tower>();
Tower.CalculateCombatTime(reloadTime, projectileSpeed);
Transform SendThis = co.transform;
Tower.SetTarget(SendThis);
}
}
}
}
and
public class Tower : MonoBehaviour {
private Transform target;
private float fireSpeed;
private double nextFireTime;
private GameObject bullet;
private Attacker source;
// Use this for initialization
public virtual void Start () {
source = this.GetComponent<Attacker> ();
}
// Update is called once per frame
public virtual void Update () {
if (target) {
Debug.Log("I have a target");
//if(nextFireTime <= Time.deltaTime)
FireProjectile ();
}
}
public void CalculateCombatTime(float time, float speed){
Debug.Log("Calculate Combat Speed");
nextFireTime = Time.time + (time * .5);
fireSpeed = speed;
}
public void SetTarget(Transform position){
Debug.Log("Set Target");
target = position;
}
public void FireProjectile(){
Debug.Log("Shoot Projectile");
bullet = (GameObject)Instantiate (source.ammoPrefab, transform.position, source.ammoPrefab.transform.rotation);
float speed = fireSpeed * Time.deltaTime;
bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, speed);
}
}
Basicly Attacker detects the object that collides with it, then if its tag is Tower it will send the information to Tower. My debug shows that every function works, even "Debug.Log("Shoot Projectile");" shows up.
However it doesn't move towards my target so I guess "bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, step);" is never being executed?
Vector3.MoveTowards only moves the object once, it's just a instant displacement when the FireProjectile is called.
You need to create some kind of projectile script with an Update() function to make it move over time.
Here is an example:
public class Projectile : MonoBehaviour
{
public Vector3 TargetPosition;
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, TargetPosition, speed * Time.DeltaTime);
}
}
Then right after your bullet instantiation, set the target:
bullet.GetComponent<Projectile>().TargetPosition = target.position;
Hope it helps.
You have to update the position of the bullet. You are only moving when you create the bullet.
Try to make a list of bullets and use the update function to change the position.

Categories