There is no Rigidbody 2D attached to the game object but the script is trying to access it - c#

Here is the code I'm using. It throws a MissingComponentException: There is no Rigidbody 2D attached to "Bird" game object but the script is trying to access it.
using System.Collections;
public class Bird : MonoBehaviour
{
public float UpForce; //Upward force of the "flap".
private bool _isDead = false; //Has the player collided with a wall?
private Animator _anim; //Reference to the Animator component.
private Rigidbody2D _rb2d; //Holds a reference to the Rigidbody2D component of the bird.
void Start()
{
//Get reference to the Animator component attached to this GameObject.
_anim = GetComponent<Animator> ();
//Get and store a reference to the Rigidbody2D attached to this GameObject.
_rb2d = GetComponent<Rigidbody2D>();
}
void Update()
{
//Don't allow control if the bird has died.
if (_isDead == false)
{
//Look for input to trigger a "flap".
if (Input.GetMouseButtonDown(0))
{
//...tell the animator about it and then...
_anim.SetTrigger("Flap");
//...zero out the birds current y velocity before...
_rb2d.velocity = Vector2.zero;
// new Vector2(rb2d.velocity.x, 0);
//..giving the bird some upward force.
_rb2d.AddForce(new Vector2(0, UpForce));
}
}
}
void OnCollisionEnter2D(Collision2D other)
{
// Zero out the bird's velocity
_rb2d.velocity = Vector2.zero;
// If the bird collides with something set it to dead...
_isDead = true;
//...tell the Animator about it...
_anim.SetTrigger ("Die");
//...and tell the game control about it.
GameControl.instance.BirdDied ();
}
}
How do I provide the reference it wants?

The GameObject this script is attached to should have a RigidBidy2D component (because the script is using it to apply forces to GameObject). You can either add a Rigidbody2D in inspector (choose the GameObject the script is attached to and use "Add Component" menu), or make it so the script adds a component automatically when you attach it to GameObject by adding a RequireComponent attribute like this:
[RequireComponent(typeof(Rigidbody2D))]
public class Bird : MonoBehaviour
{
//your Bird class code here
}
See also these questions - that's the same problem.

Related

How do I make an instantiated object keep following my player after its instantiated?

I drew a little red animation thing for when my player gets hit. I have an arrow shooter in my level but my problem is the red damage effect instantiates where my player is at, but when my player moves the object stays in the player's old position. Like lets say my player is at x:10. The effect will spawn at x:10 but when I move to x:17 the effect will stay at x:10.
Here's my code
public int damagedealt;
public Player playerscript;
public GameObject RedDamage;
public Vector2 playerpos;
public GameObject player;
// Start is called before the first frame update
void Start()
{
playerscript = FindObjectOfType<Player>();
}
// Update is called once per frame
void Update()
{
playerpos = player.transform.position;
player = GameObject.FindGameObjectWithTag("Player");
transform.Translate(Vector2.right * speed * Time.deltaTime);
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
playerscript.health -= damagedealt;
Instantiate(RedDamage, playerpos, transform.rotation);
Destroy(gameObject);
}
}
and if it's useful at all here's the damage effect script
{
public float duration;
// Start is called before the first frame update
void Start()
{
duration = .7f;
}
// Update is called once per frame
void Update()
{
duration -= Time.deltaTime;
if (duration <= .13f )
{
Destroy(gameObject);
}
}
}
You can either add a follow script to the object you would like to follow your player or you can child the object to the player. When an object is childed to another, its transform becomes relative to its parent. In your case, your instantiated object would move relative to your player.
As this seems like a temporary effect that is repeated multiple times, it might make sense to child an empty object for the sole purpose of spawning your effect there. Create an empty gameObject that is childed (meaning placed under in the scene hierarchy) to your player object. Make sure to set the position of this new empty object in a place where you would like to spawn your object.
Once you are happy with where the object is placed, you can alter your Instantiate slightly to fit the new approach.Instantiate has a few overloads to the method. The one I will use is as follows
public static Object Instantiate(Object original, Transform parent);
Instead of specifying a rotation, position, etc, you will specify the parent at which the object will be placed and childed to. Here is what the script would now look like
public int damagedealt;
public Player playerscript;
public GameObject RedDamage;
public Vector2 playerpos;
public GameObject player;
// serializing a field will expose it in the editor even if marked private
// while not allowing other scripts to access it
[SerializeField] private Transform DisplayDamageFeedback = null;
// Start is called before the first frame update
void Start()
{
playerscript = FindObjectOfType<Player>();
}
// Update is called once per frame
void Update()
{
playerpos = player.transform.position;
player = GameObject.FindGameObjectWithTag("Player");
transform.Translate(Vector2.right * speed * Time.deltaTime);
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
playerscript.health -= damagedealt;
Instantiate(RedDamage, DisplayDamageFeedback);
Destroy(gameObject);
}
}
Make sure to assign the reference to DamageFeedback in the inspector to the empty gameObject you created earlier. If you need help on any of the steps just let me know.
Try something like this.
in the effects' 'Update' script:
GameObject player = GameObject.FindGameObjectWithTag("Player");;
public void MoveGameObject()
{
transform.position = new Vector3(player.transfom.position.x,player.transform.position.y,player.transform.position.z);
}
This means that every Update, the player object is found, and the effect's position is set to match the players.
See the following link for Unity's example of position:
https://docs.unity3d.com/ScriptReference/Transform-position.html

Transform position don't change gameObject position

I want to make respawn my player to the spawn point when he has a contact with a projectile. The event onTriggerEnter is triggered when the event occurs, but the player position do not changed.
I tried to solve my problem with other.transform.position = spawnPoint; and other.gameObject.transform.localPosition = spawnPoint; or other.transform.localPosition = spawnPoint; but it does not change anything.
Here is my code :
using UnityEngine;
public class Projectil : MonoBehaviour {
private Vector3 spawnPoint;
void Start() {
spawnPoint = GameObject.Find("SpawnPoint").GetComponent<Transform>().localPosition;
}
void OnTriggerEnter(Collider other) {
if(other.CompareTag("Player")) {
other.gameObject.transform.position = spawnPoint;
Debug.Log("touched : " + other.transform.position );
}
}
}
When two objects with a physical collider collide, OnCollisionEnter is called. Only when one of them is not physical, i.e. Is Trigger is set on the collider, then OnTriggerEnter is called.
Related Unity Documentation pages:
Scripting - Collider.isTrigger
Manual - Sphere Collider

Destroy particle system when initalised programmatically

I have a game with a player and an enemy.
I also have a particle system for when the player dies, like an explosion.
I have made this particle system a prefab so I can use it multiple times per level as someone might die a lot.
So in my enemy.cs script, attached to my enemy, I have:
public GameObject deathParticle;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player" && !player.dead){
player.dead = true;
Instantiate(deathParticle, player.transform.position, player.transform.rotation);
player.animator.SetTrigger("Death");
}
}
So this plays my particle system when the player gets killed by an enemy. Now on my player script, I have this. This specific function gets played after the death animation:
public void RespawnPlayer()
{
Rigidbody2D playerBody = GetComponent<Rigidbody2D>();
playerBody.transform.position = spawnLocation.transform.position;
dead = false;
animator.Play("Idle");
Enemy enemy = FindObjectOfType<Enemy>();
Destroy(enemy.deathParticle);
}
This respawns the player like normal but in my project each time I die I have a death (clone) object which I don't want. The last 2 lines are meant to delete this but it doesn't.
I have also tried this which didn't work:
Enemy enemy = FindObjectOfType<Enemy>();
ParticleSystem deathParticles = enemy.GetComponent<ParticleSystem>();
Destroy(deathParticles);
There is no need to Instantiate and Destroy the death particle, that will create a lot of overhead, you can simply replay it when you want it to start and stop it, when you dont need it
ParticleSystem deathParticleSystem;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticleSystem.time = 0;
deathParticleSystem.Play();
}
public void RespawnPlayer()
{
//rest of the code
deathParticleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
}
or you can enable and disable the gameObject associated with your particle prefab
public GameObject deathParticlePrefab;
private void OnTriggerEnter2D(Collider2D collision)
{
#rest of the code
deathParticlePrefab.SetActive(true);
}
public void RespawnPlayer()
{
//rest of the code
deathParticlePrefab.SetActive(false);
}
You could always create a new script and assign it to the prefab that destroys it after a certain amount of time:
using UnityEngine;
using System.Collections;
public class destroyOverTime : MonoBehaviour {
public float lifeTime;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
lifeTime -= Time.deltaTime;
if(lifeTime <= 0f)
{
Destroy(gameObject);
}
}
}
So in this case you would assign this to your deathParticle. This script is useful in a multitude of scenarios if you're instantiating objects so that you don't have a load of unnecessary objects.

Unity Engine - Instantiate a prefab by collision

I have a GameObject called Player.
Attached to Player there is a script component called Player ( same )
Inside the Player's script, I have a field called _weaponPrefab ( GameObject type )
In the Inspector, I can easily drag & drop any prefabs from my Prefab folder, inside the _weaponPrefab variable.
All good so far. What I want to archive is: to be able to add my Prefabs based on a Collision2D. So if my Player collides with a Prefab, let's say a Sword, the prefab of that sword will be automatically attached and insert into the _weaponPrefab field inside the Player script.
Below my Player script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField] private float _speed = 5.0f;
[SerializeField] private float _fireRate = 0.2f;
private bool _canFire = true;
[SerializeField] private GameObject _weaponPrefab; <- to populate runtime
// Use this for initialization
void Start ()
{
transform.position = Vector3.zero;
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown(KeyCode.Space) && _canFire)
StartCoroutine(Shoot());
}
void OnTriggerEnter2D(Collider2D other)
{
//here i don't know how to continue.
}
public IEnumerator Shoot()
{
_canFire = false;
Instantiate(_weaponPrefab, transform.position + new Vector3(0, 1, 0), Quaternion.identity);
yield return new WaitForSeconds(_fireRate);
_canFire = true;
}
}
Edit: i just wrote a comment of where i don't know how to proceed.
There are many ways to accomplish what you desire.
My suggestion may be out of your comfort zone at first, but it will provide you with most flexibility and eventually easy of programming/ designing/ maintaining your game (in my opinion).
First make a scriptable object (what is a scriptable object and how do i use it?)
using UnityEngine;
using System.Collections;
using UnityEditor;
public class Item
{
[CreateAssetMenu(menuName = "new Item")]
public static void CreateMyAsset()
{
public GameObject prefab;
// you can add other variables here aswell, like cost, durability etc.
}
}
Create a new item (in Unity, Assets/new Item)
Then create a monobehaviour script which can hold the item. In your case let's name it "Pickup".
public class Pickup : MonoBehaviour
{
public Item item;
}
Finally in your player script, change your on TriggerEnter to:
void OnTriggerEnter2D(Collider2D other)
{
if(other.GetComponentInChildren<Pickup>())
{
var item = other.GetComponentInChildren<Pickup>().item;
_weaponPrefab = item.prefab;
}
}
When you instantiate a GameObject you need to pass basically 3 parameters (However not all of them are mandatory, check):
The prefab
The position
The rotation
Let's assume you have a placeholder in the hand or in the back of your character, to keep there the weapon once collected. This placeholder can be an empty gameobject (no prefab attached, but will have a transform.position component)
Now let's assume you have a list of weapons in the scene with their prefab and with a different tag each. Then you can do something like this:
GameObject weapon;
GameObject placeholder;
public Transform sword;
public Transform bow;
...
void OnTriggerEnter2D(Collider2D other)
{
//You can use a switch/case instead
if(other.gameObject.tag == "sword"){
Instantiate(sword, placeholder.transform.position, Quaternion.identity);
}else if(other.gameObject.tag == "bow"){
Instantiate(bow, placeholder.transform.position, Quaternion.identity);
}...
}
What I'll do is load the prefabs first in a Dictionary<string, GameObject> from the resources folder.
First I populate the dictionary with all the weapon prefabs in the Start method with the tag of the object as a key. Then in the OnTriggerEnter2D I check if the tag is in the dictionary then I instantiate it from there.
Take a look at the code :
private Dictionary<string, GameObject> prefabs;
// Use this for initialization
void Start () {
var sword = (GameObject) Resources.Load("Weapons/sword", typeof(GameObject));
prefabs.Add("sword", sword);
// Add other prefabs
};
void OnTriggerEnter2D(Collider2D other)
{
if (prefabs.ContainsKey(other.tag))
Instantiate(prefabs[other.tag]);
}
NOTE : your prefabs needs to be in the folder Assets/Resources/Weapons because I used Resources.Load("Weapons/sword", typeof(GameObject));

Button click does not work correctly

I don't know what i am doing wrong it's a 2D project there are two objects.One has a RigidBody2D and BoxCollider2D component. Second object only has BoxCollider2D. And bottom there is a button when press button Object1 fall on Object2 and Destroy and Instantiate Object1 again. But when Object1 Instantiate then click on button does not work. And error came up like this :
The object of type Rigidbody2D has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.
Object 1:
Object 2:
Button Click:
Object 1 Script:
public class Object1 : MonoBehaviour {
public static Object1 instance;
[SerializeField]
private Rigidbody2D body;
[SerializeField]
private bool hasdropped;
[SerializeField]
private bool click;
[SerializeField]
private float PointerPos;
[SerializeField]
private float BorderX;
void Awake(){
if (instance == null) {
instance = this;
}
//take width of screen
Vector3 gameScreen = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height,0));
BorderX = gameScreen.x - 0.6f;
body.isKinematic = true;
click = true;
hasdropped = true;
}
void FixedUpdate () {
if (click) {
Vector3 temp = transform.position;
PointerPos = Camera.main.ScreenToWorldPoint(Input.mousePosition).x;
temp.x = Mathf.Clamp(PointerPos,-BorderX,BorderX);
body.position = temp;
if(hasdropped){
return;
}
}
}
public void ButtonClick(){
body.isKinematic = false;
}
}
Object 2 Script:
public class Object2 : MonoBehaviour {
[SerializeField]
private GameObject BallClone;
void OnCollisionEnter2D(Collision2D target){
Destroy (target.gameObject);
Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity);
}
}
The problem is that you referenced the Object1Prefab (the live object, not the prefab) on ClickButton On Click(). (But referencing the prefab will not work at all)
Since you destroy it, you missed the link. (Check it: Select the ClickButton object on the hierarchy, there is the script reference, right? Click the game button once. Now deselect and select ClickButton object on hierarchy again... it is gone.)
Try to make a listener or to put the ButtonClick() method inside the object2 (that you don't destroy):
using UnityEngine;
using System.Collections;
public class Object2 : MonoBehaviour {
[SerializeField]
private GameObject BallClone;
public GameObject mine;
void OnCollisionEnter2D(Collision2D target){
Destroy (target.gameObject);
mine = Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity) as GameObject;
}
public void ButtonClick(){
//this is just an example. You might wanna to cache this info for better practice and not call GetComponent all the time
mine.GetComponent<Rigidbody2D> ().isKinematic = false;
}
}
1- Drag GameObject1Prefab (live object) to the Object2 script on "mine" field. Reference to public GameObject mine.
The first click you gonna destroy it, and create a new one and programmatically referencing the new one again on the line:
mine = Instantiate (BallClone,new Vector3(0f,2f,59f),Quaternion.identity) as GameObject;
On the ClickButton Object, you drag the GameObject2, not the 1, on the On Click().
Is this clear? Sorry my english. :~

Categories