Unity 3D Rigidbody 2D movement using MovePosition - c#

Working on 2D mode, I have a script attached to a Sprite. The Update() part is as follow:
void Update () {
if (Input.GetKeyDown ("left")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedX);
} else if (Input.GetKeyDown ("right")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedX);
} else if (Input.GetKeyDown ("up")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position + speedY);
} else if (Input.GetKeyDown ("down")) {
cursor.rigidbody2D.MovePosition (cursor.rigidbody2D.position - speedY);
}
}
where cursor is a Prefab linked in Inspector. The cursor prefab has only 2 components, Sprite Renderer (to define the image), and the Rigidbody 2D, which setting is as follow:
Mass = 1
Linear Drag = 0
Angular Drag = 0
Gravity Scale = 0
Fixed Angle = true
Is Kinematic = false
Interpolate = None
Sleeping Mode = Start Awake
Collision Detection = Discrete
But when I press the arrow keys, the sprite showing on screen does not move. What did I miss?
Tried to add Debug.Log() inside the if case, it really enters the case. And no error occurred.
Note: speedX = new Vector2(1,0); and speedY = new Vector2(0,1);

You're trying to move an object that doesn't exist from the game's perspective. Assigning cursor a prefab by dragging it from the assets library, is like assigning a blueprint. Telling the prefab to move is like yelling at a car's blueprint to accelerate :)
There are two general solutions.
1) Save a reference
Instantiate the prefab and save the resulting reference to manipulate your new object. You can directly cast it to Rigidbody2D if you're mainly interested in using the rigidbody. The cast will only work if your prefab variable is of the same type, otherwise it will raise an exception. This way will ensure that your prefab always contains a Rigidbody2D or you can't even assign it in the editor.
public Rigidbody2D cursorPrefab; // your prefab assigned by the Unity Editor
Rigidbody2D cursorClone = (Rigidbody2D) Instantiate(cursorPrefab);
cursorClone.MovePosition (cursorClone.position - speedX);
2) Assign the script to the prefab
Depending on your game and what you want to achieve, you can also add a script directly to the prefab. This way every instance of it would just control itself.
void Update () {
if (Input.GetKeyDown ("left")) {
rigidbody2D.MovePosition (rigidbody2D.position - speedX);
} else { //...}
}

Related

Unsure how to script my AI GameObject to aim at player with bullet, taking into account for gravity and drag (UNITY)

I'm currently working on a unity project where I'm testing out how to create a projectile trajectory calculation method, which'll be applied to my overall larger Unity project. Inside of this sub-project is simply a ground plane, a square target GameObject, and my AI Enemy GameObject. For reference, here's what the hierarchy of my enemy GameObject looks like:
Enemy (capsule)
GunPivot (empty, is an anchor to rotate the Gun GameObject)
Gun (square)
BulletSpawn (empty, is the spawn point for any instantiated bullet)
Now how my bullet works inside of this scene is that my bullet has a rigidbody, and moves through the scene using the Unity's physics engine, and my gun GameObject has the script that spawns, aligns, and shoots the bullet forward. It just uses AddForce() and a FirePower variable, which can be set in the unity editor. Below is what the code looks like:
public class EnemyGunController : MonoBehaviour
{
//
// Properties & Fields
//
public GameObject BulletPrefab;
public GameObject BulletSpawn;
public float FireRate;
public float FirePower;
public bool CanFire;
//
// Method(s)
//
void Update()
{
StartCoroutine(Fire());
}
// Fires a bullet.
IEnumerator Fire()
{
if (CanFire)
{
CanFire = false;
// Instantiate a bullet. Set the rotation to be looking forwards relative to BulletSpawn
GameObject bullet = Instantiate(BulletPrefab, BulletSpawn.transform.position,
Quaternion.LookRotation(BulletSpawn.transform.up));
// Set bullet so that it ignores collisions with the gun.
Physics.IgnoreCollision(bullet.GetComponent<Collider>(), GetComponent<Collider>());
// Apply force to the bullet.
bullet.GetComponent<Rigidbody>().AddForce(BulletSpawn.transform.forward * FirePower, ForceMode.Impulse);
// Wait for the fire rate before firing again.
yield return new WaitForSeconds(FireRate);
CanFire = true;
}
}
}
Now here's where the issue now lies. I want my Enemy GameObject to hold a method that'll calculate at what rotational value GunPivot needs to be at in order for the Bullet to hit the target GameObject. I already know that Quaternion.LookAt() will rotate the gun to where the target is, but that only solves half of the issue. That rotates the gun horizontally to where the gun is (y-axis I believe), but now I need to check at which rotational value GunPivot needs to be at so that the bullet, which is a rigidbody and has non-zero values for its mass and drag, will hit the target GameObject.
I did look into this a bit and found out the Physics.Simulate() method and how I can calculate this in a separate scene, however when I ran the unity editor, it just froze and then crashed. I attached it to a separate GameManager GameObject, and here's the code for it below:
public class BulletPredictor : MonoBehaviour
{
//
// Properties & Fields
//
public GameObject BulletPrefab;
Scene _mainScene;
Scene _bulletPredictorScene;
PhysicsScene _mainPhysicsScene;
PhysicsScene _bulletPredictorPhysicsScene;
//
// Method(s)
//
void Start()
{
// Disable auto simulation.
Physics.autoSimulation = false;
// Set the main scene and main physics scene fields.
_mainScene = SceneManager.GetSceneByName("MainScene");
_mainPhysicsScene = _mainScene.GetPhysicsScene();
// Set the bullet predictor scene and bullet predictor physics scene fields.
CreateSceneParameters bulletPredictorSceneParameters = new(LocalPhysicsMode.Physics3D);
_bulletPredictorScene = SceneManager.CreateScene("BulletPredictorScene", bulletPredictorSceneParameters);
_bulletPredictorPhysicsScene = _bulletPredictorScene.GetPhysicsScene();
}
// FixedUpdate is called once per frame (fixed at 50 fps).
void FixedUpdate()
{
// Main scene physics need to work, so simulate main scene normally. Needs to be within a fixed update because variable fps can effect the physics simulation.
if (_mainPhysicsScene.IsValid())
{
_mainPhysicsScene.Simulate(Time.fixedDeltaTime);
}
}
// Simulates various shot trajectories to find an optimal shooting angle to hit target, then rotates enemy's gun in main scene to that angle.
public Quaternion ShootBullet(GameObject shooter, GameObject target, Vector3 shooterPos, Vector3 targetPos)
{
// Checks if the main or bullet predictor physics scene is valid. If it isn't just return a default rotation.
if (!_mainPhysicsScene.IsValid() || !_bulletPredictorPhysicsScene.IsValid())
return Quaternion.identity;
// Clone shooter, and move it to the bullet predictor scene.
GameObject shooterClone = Instantiate(shooter, shooterPos, Quaternion.identity);
SceneManager.MoveGameObjectToScene(shooterClone, _bulletPredictorScene);
// Also clone target, and move it to the bullet predictor scene.
GameObject targetClone = Instantiate(target, targetPos, Quaternion.identity);
SceneManager.MoveGameObjectToScene(targetClone, _bulletPredictorScene);
// Get a reference to shooterClone's GunPivot gameobject.
GameObject gunPivot = shooterClone.transform.Find("GunPivot").gameObject;
// Rotate gunPivot to face targetClone.
gunPivot.transform.LookAt(targetClone.transform);
while (gunPivot.transform.rotation.x > -90)
{
// Instantiate a bullet, and move it to the bullet predictor scene. Set the rotation to be the shooterClone's BulletSpawn's rotation.
GameObject bullet = Instantiate(BulletPrefab, gunPivot.transform.position, Quaternion.LookRotation(gunPivot.transform.up));
// Add force to the bullet.
bullet.GetComponent<Rigidbody>().AddForce(gunPivot.transform.forward * bullet.GetComponent<BulletBehavior>().InitialSpeed, ForceMode.Impulse);
// Loop for 200 iterations, and simulate the bullet.
for (int i = 0; i < 200; i++)
{
// Simulate bullet predictor scene.
_bulletPredictorPhysicsScene.Simulate(Time.fixedDeltaTime);
}
// if the bullet is destroyed, break.
if (bullet == null)
{
break;
}
Destroy(bullet);
gunPivot.transform.Rotate(-0.1f, 0, 0);
}
return gunPivot.transform.rotation;
}
}
For debugging purposes, I just added a variable that referenced the GunPivot on my Gun GameObject, and called the ShootBullet() before I actually started the Fire() method, but my unity editor ended up crashing every single time I ran with this code. I'm not entirely sure why this didn't work, although I believe that this happened since I was using Simulate() in a numerical calculation, which probably is VERY VERY computationally expensive.
I'd really appreciate any insight or tips on how to either improve the simulate code I have above or perhaps try out a different approach from it what I'm doing. Also FYI, you can assume that the target isn't moving, so that might make things easier I suppose?

Prefab loaded by Resources.Load can not be rendered on scene

I have created a Prefab and I load it in the Awake function with the following code:
GameObject bulletPrefab = Resources.Load<GameObject>("Enemy/Bullet");
bulletPrefab.transform.position = new Vector3(0,0,0);
The problem is that the bulletPrefab is not gonna show in the game scene. Its activeSelf property is true but its activeInHierarchy property is false. Does anyone know why it is like this and how to make the bulletprefab show in the scene?
Do not modify a prefab. You tried to modify it when you did bulletPrefab.transform.position = ...
The bulletPrefab is a loaded GameObject which is only stored in the memory. To see it, you have to instantiate it with the Instantiate function. You seem to have done this in your other question but for some reason decided to remove that critical part in this question.
Looking at your last question, it seems like your issue is shooting the prefab. You don't shoot the prefab by setting the bullet's position to another position in one frame. You can use a coroutine and do that over multiple frames or you can use Rigidbody for this. I suggest using Rigidbody because that's the kind of stuff it is made for.
Makes sure that Rigidbody is attached to the prefab you want to load. Load the prefab, instantiate it then get the Rigidbody attached to it. Move the bullet to the front of the player + camera then use Rigidbody.velocity or Rigidbody.AddForce to shoot the bullet to the CameraTransform.forward direction so that the bullet will travel to the direction the camera is facing.
See below for example of loading and shooting a bullet prefab when space key is pressed.
GameObject bulletPrefab;
Transform cameraTransform;
public float bulletSpeed = 300;
private void Start()
{
//Load Prefab
bulletPrefab = Resources.Load<GameObject>("Enemy/Bullet");
//Get camera transform
cameraTransform = Camera.main.transform;
}
void Update()
{
//Shoot bullet when space key is pressed
if (Input.GetKeyDown(KeyCode.Space))
{
shootBullet();
}
}
void shootBullet()
{
//Instantiate prefab
GameObject tempObj = Instantiate(bulletPrefab) as GameObject;
//Set position of the bullet in front of the player
tempObj.transform.position = transform.position + cameraTransform.forward;
//Get the Rigidbody that is attached to that instantiated bullet
Rigidbody projectile = GetComponent<Rigidbody>();
//Shoot the Bullet
projectile.velocity = cameraTransform.forward * bulletSpeed;
}

What code do I need for my Unity character to shoot the bullet correctly?

I am not new to programming, but I am new to C#. I am experienced in Python, Java, and HTML. My game is 2D I have a game where my character currently has to touch the enemy to kill it. Now I added code for shooting a bullet to kill the enemy. I also want the bullet to be shot if the spacebar is pressed. The character is supposed to a shoot bullet in either direction. I have taken my code from an example my professor gave me which was originally Javascript and I converted it to C#. Unity no longer supports Javascript. The example code he gave me was basically a rocket shooting as many bullets as I clicked (clicking the mouse shoots bullets) to eliminate an enemy, however the rocket in that example does not move. In my game, the character moves, so the bullet has to get the position of the character. What is the correct code for getting the character position and shooting a bullet correctly?
I tested my game with my current code. The bullet is being spit out of nowhere (from the bottom of my background wallpaper [smack in the middle of the bottom] to a little below the wallpaper). Not even from the character...
Also, I added the Hit class script to my Bullet category in Unity.
Complete Camera Controller (no issues here at all)
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour {
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start ()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate ()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
Complete Player Control Class (If you read the comments in the code that say the "BULLET CODE", that's new code I have placed in the game for the bullets.
using UnityEngine;
using System.Collections;
//Adding this allows us to access members of the UI namespace including Text.
using UnityEngine.UI;
public class CompletePlayerController : MonoBehaviour
{
public float speed; //Floating point variable to store the player's movement speed.
public Text countText; //Store a reference to the UI Text component which will display the number of pickups collected.
public Text winText; //Store a reference to the UI Text component which will display the 'You win' message.
private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics.
private int count; //Integer to store the number of pickups collected so far.
Rigidbody2D bullet;
float speed2 = 30f; //BULLET CODE
// Use this for initialization
void Start()
{
//Get and store a reference to the Rigidbody2D component so that we can access it.
rb2d = GetComponent<Rigidbody2D> ();
//Initialize count to zero.
count = 0;
//Initialze winText to a blank string since we haven't won yet at beginning.
winText.text = "";
//Call our SetCountText function which will update the text with the current value for count.
SetCountText ();
}
//FixedUpdate is called at a fixed interval and is independent of frame rate. Put physics code here.
void FixedUpdate()
{
//Store the current horizontal input in the float moveHorizontal.
float moveHorizontal = Input.GetAxis ("Horizontal");
//Store the current vertical input in the float moveVertical.
float moveVertical = Input.GetAxis ("Vertical");
Rigidbody2D bulletInstance; //BULLET CODE
//Use the two store floats to create a new Vector2 variable movement.
Vector2 movement = new Vector2 (moveHorizontal, moveVertical);
//Call the AddForce function of our Rigidbody2D rb2d supplying movement multiplied by speed to move our player.
rb2d.AddForce (movement * speed);
if(Input.GetKeyDown(KeyCode.Space)&& Hit.hit == false) //BULLET CODE IN HERE
{
// ... instantiate the bullet facing right and set it's velocity to the right.
bulletInstance = Instantiate(bullet, transform.position, Quaternion.Euler(new Vector3(0,0,0)));
bulletInstance.velocity = new Vector2(speed2, 0);
bulletInstance.name = "Bullet";
}
}
//OnTriggerEnter2D is called whenever this object overlaps with a trigger collider.
void OnTriggerEnter2D(Collider2D other)
{
//Check the provided Collider2D parameter other to see if it is tagged "PickUp", if it is...
if (other.gameObject.CompareTag ("PickUp"))
{
//... then set the other object we just collided with to inactive.
other.gameObject.SetActive(false);
transform.localScale += new Vector3(0.1f, 0.1f, 0);
//Add one to the current value of our count variable.
count = count + 1;
//Update the currently displayed count by calling the SetCountText function.
SetCountText ();
}
}
//This function updates the text displaying the number of objects we've collected and displays our victory message if we've collected all of them.
void SetCountText()
{
//Set the text property of our our countText object to "Count: " followed by the number stored in our count variable.
countText.text = "Count: " + count.ToString ();
//Check if we've collected all 12 pickups. If we have...
if (count >= 12)
//... then set the text property of our winText object to "You win!"
winText.text = "You win!";
}
}
Hit code. All code in this class is made for the bullet.
using UnityEngine;
using System.Collections;
public class Hit : MonoBehaviour
{
GameObject[] gameObjects;
public static bool hit = false;
void Removal ()
{
gameObjects = GameObject.FindGameObjectsWithTag("Bullet");
for(var i= 0 ; i < gameObjects.Length ; i ++)
Destroy(gameObjects[i]);
}
void OnCollisionEnter2D ( Collision2D other )
{
if(other.gameObject.name=="Bullet")
{
Removal();
Destroy(gameObject);
hit = true;
}
}
}
I see several issues with the code you wrote. as #Kyle Delaney suggested, I'd also highly recommend you check out the Unity Learn website, and go through several tutorials before moving through. I've highlighted a few issues below that may help solve your problem, but you could really benefit from changing your approach in the first place to avoid many of these issues. See below.
In your camera controller class, why not make the offset public so you can set it yourself in the inspector>
In Player controller class:
changed:
if (Input.GetKeyDown(KeyCode.Space)) //Shoot bullet
{
// ... instantiate the bullet facing right and set it's velocity to the right.
Rigidbody2D bulletRB = Instantiate(bullet, transform.position, transform.rotation);
bulletRB.AddForce(new Vector2(speed2,0), ForceMode2D.Impulse);
}
On a click, this instantiates the bullet prefab and sets its transform to copy the position and rotation of the player. It then adds a force to it to the right. You should avoid setting the velocity manually with rigidbodies, this can cause unwanted behaviour. Use the Addforce/addTorque methods instead. The ForceMode says to ignore its mass and set its velocity.
Then delete your Hit class, and replace with this Bullet class, which you drag onto the bullet prefab. Your player shouldn't be in charge of checking for bullet hits. that's the bullet's job. The player just launches the bullets and then the bullets do what bullets do. this just checks to see if the bullet hit anything, if so it destroys it. You can make this more complicated if you want. I would recommend using layers to determine which layers the bullet checks collisions with. You probably don't want bullets destroying your terrain. and you definitely don't want bullets destroying the player itself!
public class Bullet : MonoBehaviour
{
private void OnCollisionEnter2D(Collision2D collision)
{
Destroy(collision.gameObject);
Destroy(this.gameObject);
}
}
Also you shouldn't need to set the name of the bullet, since your prefab bullet should already be named "bullet".
I hope this gets you started in the right direction. But based on your code, I HIGHLY recommend you work through the tutorials before continuing with any projects. The unity staff that make them are super helpful and the tutorials start off really simple but get really complicated fast, so you actually come away learning a ton!
Perhaps the problem has to do with the player's parent transform. You could try something like this to make sure the bullet has the right parent:
bulletInstance = Instantiate(bullet);
bulletInstance.transform.parent = transform.parent;
bulletInstance.transform.position = transform.position;
bulletInstance.velocity = new Vector2(speed2, 0);
bulletInstance.name = "Bullet";
If that doesn't work it may be worthwhile to check the player's Rect Transform to see where the anchors and pivot are. Perhaps the player's actual "position" coordinates aren't where you think they are.
Have you done the Space Shooter tutorial? It has a segment about shooting bullets.

How to have a powerup have a slight chance of spawning when killing an enemy?

so we have a class assignment and pretty much we are doing the Survival Shooter project from the Unity tutorials. I've managed to make health packs and little boxes that give you speed when you pick them up, but now I want the power-ups to have a slight chance of dropping when enemies die. Can someone help me out? I'm not really asking for entire code, I have some down below:
public float percentDrop = 50f;
public GameObject HealthPack;
void Awake()
{
HealthPack = GetComponent<GameObject>();
}
public void TakeDamage (int amount, Vector3 hitPoint) { if(isDead) return;
enemyAudio.Play ();
currentHealth -= amount;
hitParticles.transform.position = hitPoint;
hitParticles.Play();
if(currentHealth <= 0)
{
Death ();
}
}
void Death ()
{
isDead = true;
capsuleCollider.isTrigger = true;
anim.SetTrigger ("Dead");
enemyAudio.clip = deathClip;
enemyAudio.Play ();
var randChance = Random.Range(0f, 100f);
if (randChance < percentDrop)
{
//GameObject.Healthpack.setActice(true);
}
}
I'm not too sure how to make the Game Object spawn when they die, can someone help me out?
Create a "Health Pack" prefab from the editor. If you need more information about prefabs, the information is out there on the web. To create a prefab, simply drag and drop a GameObject from the scene to your project. Its name in the scene will become blue. You can delete it from the scene and it will still be in the project.
Drag the prefab from the project to the HealthPack slot in the inspector of your ennemy. The ennemy will now have a reference to the prefab.
When you want to create a new health pack, you can use the Object.Instantiate static method as specified by rutter. Here is the official doc. When you instantiate your new Health Pack, I guess you want it to appear where the ennemy is, wich means you'll want to use one of the method's overloads which takes a Vector3D position as a parameter, which will most likely be transform.position. Since those methods also ask for a Quaternion, just pass in the Quaternion.identity constant.
This is how your code could look:
if (randChance < percentDrop)
{
Object.Instantiate(HealthPack, transform.position, Quaternion.identity);
}
Another problem, as mentioned in my comment, is the Awake function: HealthPack = GetComponent<GameObject>();.
The HealthPack prefab should be assigned to the ennemy from the inspector. That line in the Awake function will assign your ennemy's GameObject component to HealthPack, which is not desirable in the current context.
I hope this helps!

Unity 2D C# - Collider not working

I am working on a 2D TopDown game in Unity 5. The collision is not working at all.
The player and the obstacle both have a 2D Collider and they are not Trigger. The player has a 2D Rigidbody with Kinematic set to false, gravity scale is equal to zero. Maybe the movement code has something to do with it.
The code is a bit long so I'll just show you the code for moving up:
if (Input.GetAxis ("Up") > 0) {
if (movingDown == false) {
posY += speed * Time.deltaTime;
movingUp = true;
}
} else {
movingUp = false;
}
/.../
transform.position = new Vector3 (posX, posY, 0);
It is always setting the value of the position as long as you are pressing the up button. Is there a way to fix this?
I think that the problem is that you are setting the position directly. So at each frame, you are telling unity exactly were the object should be, which overrides the position that would be computed from collision.
To fix this, you need to modify your movement code to add a force to your rigidbody and leave the position untouched ( see rigidbody doc, and function AddForce (https://docs.unity3d.com/ScriptReference/Rigidbody.html)
Try using
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
This replaces your system with a velocity-based system as updating the transform.postition of a rigidbody is not recommended. With the system you have, the collision is not being detected because the rigidbody isn't being updated.

Categories