How to make a Rigidbody2D bullet move in Unity2D - c#

I've been trying various fixes for days now and I cannot figure this out. I need a bullet to spawn and move until collision, but I cannot even get it to move (it spawns just fine). Note: This is a 2D game.
Here is my code:
public class Enemy : MonoBehaviour
{
public float timeBetweenFire = 2;
public Rigidbody2D bullet;
public Transform gunSpawn;
private float timer;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update() {
timer+=Time.deltaTime;
if (timer >= timeBetweenFire) {
Shoot();
timer = 0;
}
}
private void Shoot() {
Rigidbody2D bulletInstance;
bulletInstance = Instantiate(bullet, gunSpawn.position, gunSpawn.rotation) as Rigidbody2D;
//bulletInstance.AddForce(gunSpawn.forward * 1000f);
//bulletInstance.AddForce(Vector3.up * 10 * Time.deltaTime);
//bulletInstance.AddForce(transform.forward * 100);
bulletInstance.AddForce(Vector3.up * 1000);
}
}
The commented bulletInstance methods are what I've recently tried, I did a bunch of large scale code changes regarding bullet (iirc changed it around between Rigidbody, GameObject, and Transform) and nothing helped. The bullet spawns a good bit away from anything except the gunSpawn, which is just an empty GameObject, so I don't think collisions are an issue. I'm a new Unity programmer so forgive me for any dumb mistakes.
UPDATE:
This is what I did to get it (mostly) working. When the bullet collides it can spin out, but at least I made some progress.
public class Deflect : MonoBehaviour
{
private Rigidbody2D bullet;
public float bulletSpeed = 0.1f;
Vector2 direction = new Vector3(0.7f,0.7f);
ContactPoint2D[] myContact = new ContactPoint2D[1];
Vector2 _velocity;
public void Update ()
{
}
// Start is called before the first frame update
void Start()
{
bullet = this.GetComponent<Rigidbody2D>();
_velocity = bullet.velocity;
}
void OnCollisionEnter2D(Collision2D collision)
{
foreach (ContactPoint2D contact in collision.contacts)
{
print(contact.collider.name + " hit " + contact.otherCollider.name);
// Visualize the contact point
Debug.DrawRay(contact.point, contact.normal, Color.white);
}
Vector2 inNormal = collision.contacts[0].normal;
_velocity = Vector3.Reflect(_velocity, inNormal);
bullet.velocity = _velocity;
}
}

1 - Create your bullet prefab as a GameObject
2 - Add a RigidBody2D to it
3 - Make sure the isKinematic is unchecked on the Rigibody2d
4 - then
GameObject bulletInstance = Instantiate(bullet, gunSpawn.position,gunSpawn.rotation);
bulletInstance.GetComponent<Rigidbody2D>().AddForce(Vector3.up * 1000);
IsKinematic prevents the Rigidbody2D to be affected by forces .
https://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html

Make sure that the bullet has a rigid-body component and that it is not set to kinematic.

Related

How to respawn a paddle and ball in breakout - Unity 3D C#

I'm making a breakout game in 3D and it's my first time making a game and using Unity so I'm a bit clueless. I've got to the point where my game works fine up until the ball goes off the screen and into the "dead zone".
Can someone advise how to respawn the paddle and ball together and carry on with the game?
I've included my ball and paddle scripts below, I have a script for the bricks as well but not sure that was relevant. I also made a prefab of the ball and paddle together but no idea what to do with it.
Thanks to anyone who can help :)
Code for my ball
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallScript : MonoBehaviour
{
public Rigidbody rbody;
public float MinVertMovement = 0.1f;
public float MinSpeed = 10f;
public float MaxSpeed = 10f;
private bool hasBeenLaunched = false;
void Start()
{
}
private float minVelocity = 10f;
private Vector3 lastFrameVelocity;
void FixedUpdate()
{
if (hasBeenLaunched == false)
{
if (Input.GetKey(KeyCode.Space))
{
Launch();
}
}
if (hasBeenLaunched)
{
Vector3 direction = rbody.velocity;
direction = direction.normalized;
float speed = direction.magnitude;
if (direction.y>-MinVertMovement && direction.y <MinVertMovement)
{
direction.y = direction.y < 0 ? -MinVertMovement : MinVertMovement;
direction.x = direction.x < 0 ? -1 + MinVertMovement : 1 - MinVertMovement;
rbody.velocity = direction * MinSpeed;
}
if (speed<MinSpeed || speed>MaxSpeed)
{
speed = Mathf.Clamp(speed, MinSpeed, MaxSpeed);
rbody.velocity = direction*speed;
}
}
lastFrameVelocity = rbody.velocity;
}
void OnCollisionEnter(Collision collision)
{
Bounce(collision.contacts[0].normal);
}
private void Bounce(Vector3 collisionNormal)
{
var speed = lastFrameVelocity.magnitude;
var direction = Vector3.Reflect(lastFrameVelocity.normalized, collisionNormal);
Debug.Log("Out Direction: " + direction);
rbody.velocity = direction * Mathf.Max(speed, minVelocity);
}
public void Launch()
{
rbody = GetComponent<Rigidbody>();
Vector3 randomDirection = new Vector3(-5f, 10f, 0);
randomDirection = randomDirection.normalized * MinSpeed;
rbody.velocity = randomDirection;
transform.parent = null;
hasBeenLaunched = true;
}
}
Code for my paddle
public class PaddleScript : MonoBehaviour
{
private float moveSpeed = 15f;
void Start()
{
}
void Update()
{
if (Input.GetKey(KeyCode.RightArrow) && transform.position.x<9.5)
transform.Translate(moveSpeed *Input.GetAxis("Horizontal")*Time.deltaTime, 0f, 0f);
if (Input.GetKey(KeyCode.LeftArrow) && transform.position.x>-7.5)
transform.Translate(moveSpeed *Input.GetAxis("Horizontal")*Time.deltaTime, 0f, 0f);
}
}
The simplest thing you can do to check wether the ball goes off screen is to place a trigger immediately off the perimeter of the camera, and add an OnTriggerEnter2D method to your ball.
/* Inside the ball script */
private void OnTriggerEnter() { // Use the 2D version if you're using 2D colliders
/* Respawning stuff */
}
Since you may want a bunch of different things to happen when that method triggers, you may want to use a Unity Event, which is not the king of performance but it probabily doesn't matter for a game like breakout.
using UnityEngine.Events;
public class BallScript : MonoBehaviour
{
public UnityEvent onBallOut;
/* ... */
private void OnTriggerEnter() {
onBallOut.Invoke();
}
}
You then probabily want a Respawn() method (not Reset() because that's a default MonoBehaviour call) which places the ball back to its original position, which you can store in a field as soon as the scene loads:
private Vector3 defaultPosition;
private void Start() {
defaultPosition = transform.position;
}
PS: If you aren't using the Start() method in your paddle script then remove it, cause it will be called by Unity even if empty.

Cant Change Friction of Player In Unity2D

I am extremely new to both Unity and C# and have been working on it for a few days. I'm currently trying to stop my player from sliding and to do this I've set the friction value of the players material high so that it doesn't slide. This however creates an issue where my character travels entirely too fast. To get around this I created a child object with a BoxCollider2D tagged as Friction Controller that I can modify. I get a code that changes the friction value of the physics material to 0 when i start moving and 100 when am supposed to stop. The problem is that while this updates the material itself it does not affect the box colliders settings. Does anybody know a solution for this?
using System.Collections.Generic;
using System.Collections.Specialized;
using UnityEngine;
public class Player_Movement : MonoBehaviour
{
GameObject frictionController;
public BoxCollider2D collider;
public float speed = 400f;
public float jumpForce;
private float friction;
private Rigidbody2D rb2d;
private bool isMoving;
// Start is called before the first frame update
void Start()
{
rb2d = GetComponent<Rigidbody2D> ();
}
// Update is called once per frame
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
Vector2 movement = new Vector2(moveHorizontal,0);
rb2d.AddForce(movement * speed);
}
void Update()
{
frictionController = GameObject.FindWithTag("Friction Controller");
collider = frictionController.GetComponent<BoxCollider2D>();
if (Input.GetKey("a") || (Input.GetKey("d")))
{
{ Debug.Log("Pressed Button"); }
collider.sharedMaterial.friction = 0;
} else { collider.sharedMaterial.friction = 100; }
///This part isn't complete yet
float moveVertical = Input.GetAxis("Vertical");
Vector2 jump = new Vector2(0, moveVertical);
if (Input.GetKeyDown("space"))
{
rb2d.AddForce(Vector3.up * jumpForce);
}
}
}
I'm not sure that your approach is a particularly good one and is likely to give you problems later on. Since you're using the physics system, a better approach would be to apply a force to your Rigidbody that is the OPPOSITE of its velocity when want it to come to a stop.
Nevertheless here is a solution that effectively does what you want to do using a similar approach to what you're attempting. Rather than manipulating the physics material properties, this solution manipulates the drag value of the rigidbody.
public class Player_Movement : MonoBehaviour
{
private Rigidbody2D rb2d;
private float speed = 100f;
void Start()
{
rb2d = GetComponent<Rigidbody2D> ();
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
Vector2 movement = new Vector2(moveHorizontal,0);
rb2d.AddForce(movement * speed);
}
void Update()
{
if (Input.GetKey(KeyCode.A) || (Input.GetKey(KeyCode.D)))
{
rb2d.drag = 5; // Adjust this value to modify speed
}
else
{
rb2d.drag = 100; // Adjust this value to modify slippery-ness
}
}
}

How can I apply scripts in new camera which is child of a gameobject

Earlier I was facing problem regarding the unity camera problem it always stuck on 0,0,0.08 and also find a solution so I first create an empty gameobject and then drag the camera in that empty gameobject but after doing this the scripts which I applied to the gameobject is working fine but the script which I place in camera is not working at all
Camera Script
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
void Update()
{
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
}
Player Script
public float speed = 4.5f;
public float JumpingForcec = 450f;
void Update()
{
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
First of all when dealing with a Rigidbody (or the Physics in general) you shouldn't set a position directly through the Transform component but rather use Rigidbody.position or in your case for a smooth movement even rather Rigidbody.MovePosition, both in FixedUpdate.
In general anything related to the Physics (so also everything using Rigidbody) should be done in FixedUpdate while the check for GetKeyDown has to be done in Update.
PlayerScript
public class PlayerScript : MonoBehaviour
{
public float speed = 4.5f;
public float JumpingForcec = 450f;
// If possible reference this in the Inspector already
[SerializeField] private Rigidbody rigidBody;
private bool jumpWasPressed;
private void Awake()
{
if (!rigidBody) rigidBody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
rigidBody.MovePosition(transform.position + speed * Vector3.forward * Time.deltaTime);
if (!jumpWasPressed) return;
Debug.Log("SPace was pressed");
rigidBody.AddForce(Vector3.up * JumpingForcec);
jumpWasPressed = false;
}
private void Update()
{
// Note that currently you can multijump .. later you will want to add
// an additional check if you already jump again
if (Input.GetKeyDown(KeyCode.Space)) jumpWasPressed = true;
}
}
Make sure that Is Kinematic is disabled in the Rigidbody component! Otherwise AddForce is not processed.
If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore.
The camera movement I would move to LateUpdate in order to make sure it is the last thing calculated after the other Update calls have finished. Especially after all user input has been processed (in your case maybe not that relevant since movement is processed in FixedUpdate but in general).
Second problem: Here you are not taking the changed Y position by jumping into account so rather add the "wobbling" effect to the player's transform.position.y and rather use the localPosition for the Camera:
public class CameraScript : MonoBehaviour
{
public float MovementAmplitude = 0.1f;
public float MovementFrequency = 2.25f;
// reference the player object here
public Transform playerTransform;
private float originalLocalPosY;
private void Start()
{
if(!playerTransform) playerTransform = transform.parent;
originalLocalPosY = transform.localPosition.y;
}
private void LateUpdate()
{
transform.localPosition = Vector3.up * (originalLocalPosY + Mathf.Cos(playerTransform.position.z * MovementFrequency) * MovementAmplitude);
}
}
Maybe you want to disable the wobbling effect during a jump later, though ;)
Try to put all the update stuff in the same method, it should work both (theorically, not tested) so you have to fix your code in order to get what you want:
void Update() {
// Camera update
transform.position = new Vector3(
transform.position.x,
Mathf.Cos(transform.position.z * MovementFrequency) * MovementAmplitude,
transform.position.z
);
// Player update
transform.position += speed * Vector3.forward * Time.deltaTime;
if (Input.GetKeyDown("space"))
{
Debug.Log("SPace is pressed");
Debug.Log(GetComponent<Rigidbody>());
GetComponent<Rigidbody>().AddForce(Vector3.up * JumpingForcec);
}
}
Hope this helps you, cheers!

Unity crashes every time I try to stop the movement of my object

I'm making a non random generated runner game and I'm trying to code the boss entry and fight. So the game consist of an astronaut (player) that stays on screen all the time (so him, background and camera never move, well the player can move but it's clamped.
All the hazards come towards the player and he has to avoid them or defeat them until the last hazard which is the boss. The boss is at the end of the line and goes along the z-Axis as well towards the player and I want it to stop when it collides with a Quad, so the boss is static and they can fight.
After that I want the boss to move up and down and shoot the player with Lerp functions.
The code looks like this:
public class BossController : MonoBehaviour {
public float speed;
public float health;
public Animator anim;
public Transform startMarker;
public Transform endMarker;
private Rigidbody rb;
private HUDController hud;
private bool startIntro = false;
private float startTime;
private float journeyLength;
void Start () {
startTime = Time.time;
journeyLength = Vector3.Distance (startMarker.position, endMarker.position);
anim = GetComponent<Animator> ();
rb = GetComponent<Rigidbody> ();
rb.velocity = transform.forward * -speed;
}
void Update(){
//transform.position += transform.forward * -speed * Time.deltaTime;
if (startIntro) {
rb.velocity = new Vector3(0,0,0);
Fight ();
}
}
void Fight(){
float distCovered = (Time.time - startTime) * speed;
float fracJourney = distCovered / journeyLength;
while(true)
{
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
}
}
void OnTriggerEnter(Collider other){
if (other.gameObject.CompareTag ("bossEntry")) {
startIntro = true;
//anim.SetTrigger ("quad");
}
}
}
I tried first with the rigidbody and when it collides, the rigidbody's velocity is 0 and that works fine AS LONG AS the Fight() method is commented. So maybe it's because of the lerp functions?
When it;s not commented when the boss hits the collider everything freezes and I cannot use Unity anymore and I have to restart the programm.
Please help!
Edit: I removed the while(true) loop and now the boss falls straight down when it collides with the quad instead of lerping. The Boss is under an empty object and both start and end markers are under the empty object as well. Every component is attached to the empty object (parent).
try and track (print) the values of your veriables.
I'd guess you have a divisor of 0 here -
float fracJourney = distCovered / journeyLength;
if not, its most likely you have an endless loop, or a null pointer which is vital.
Sorry, I just noticed this now.
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
The second Lerp overrides the value of first one immediately. If you want something like a ping pong effect, make a flag to determine the current state you want.
bool toEndMarker = true;
void Fight() {
//Some code here
if(toEndMarker)
{
transform.position = Vector3.Lerp(startMarker.position, endMarker.position, fracJourney);
if(//DETERMINE if transform.position is already at end marker)
{
toEndMarker = false;
}
}
else
{
transform.position = Vector3.Lerp(endMarker.position, startMarker.position, fracJourney);
if(//DETERMINE if transform.position is already at start marker)
{
toEndMarker = true;
}
}
}

How to add force to an object after a collision in unity2d, to simulate hitting a baseball with a bat

I am working on a 2D game project. I would like the user to be able to hit the ball when he presses on the "space" key. I assigned;
Circle collider 2D & Rigidbody 2D to the ball
Rigidbody 2D & Box Collider 2D to the hero
Edge Collider 2D to the baseball bat.
Here is my script which I have called "KickTheBall.cs":
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour {
public float bounceFactor = 0.9f; // Determines how the ball will be bouncing after landing. The value is [0..1]
public float forceFactor = 10f;
public float tMax = 5f; // Pressing time upper limit
private float kickStart; // Keeps time, when you press button
private float kickForce; // Keeps time interval between button press and release
private Vector2 prevVelocity; // Keeps rigidbody velocity, calculated in FixedUpdate()
[SerializeField]
private EdgeCollider2D BatCollider;
private Rigidbody2D rb;
void Start () {
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate () {
if(kickForce != 0)
{
float angle = Random.Range(0,20) * Mathf.Deg2Rad;
rb.AddForce(new Vector2(0.0f,
forceFactor * Mathf.Clamp(kickForce, 0.0f, tMax) * Mathf.Sin(angle)),
ForceMode2D.Impulse);
kickForce = 0;
}
prevVelocity = rb.velocity;
}
void Update(){
if(Input.GetKeyDown (KeyCode.Space))
{
kickStart = Time.time;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if(hit.collider.name == "Ball") // Rename ball object to "Ball" in Inspector, or change name here
kickForce = Time.time - kickStart;
}
}
}
public void KickBall(){
BatCollider.enabled = true;
}
void OnCollisionEnter(Collision col)
{
if(col.gameObject.tag == "Ground") // Do not forget assign tag to the field
{
rb.velocity = new Vector2(prevVelocity.x,
-prevVelocity.y * Mathf.Clamp01(bounceFactor));
}
}
}
However, I am unable to kick the ball when I press the space key. The ball is just bouncing because of colliders. What am I missing?
Check my result:
I would advocate something more like this to start with. This is the script you would add onto your baseball bat.
Part 1:
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour
{
public float forceFactor = 10f;
private float kickForce = 50f;
void OnCollisionEnter(Collision col)
{
if(col.gameObject.tag == "Ball") // Do not forget assign tag to the field
{
rb = col.gameobject.GetComponent<Rigidbody>();
rb.AddForce(transform.right * kickForce);
}
}
}
I have simplified your AddForce function for demonstration purposes. Feel free to replace it with your more complex AddForce function if everything is working.
Part 2:
If you really want to include the part where holding the space button makes the hit stronger, then add this:
void Update()
{
if(Input.GetKey(KeyCode.Space))
{
kickForce += 0.5f;
}
}
and add at the end of the oncollisionenter
kickForce = 0;
What this will do is build up force while you hold the space button down. After a successful hit the force will reset to 0. So subsequent collisions will not result in a hit until the space button is held again.
Let me know if this did anything for you.
I solved the issue with the help of #TylerSigi. I updated my script file with these codes:
using UnityEngine;
using System.Collections;
public class KickTheBall : MonoBehaviour {
public float forceFactor = 10f;
private float kickForce = 0f;
private EdgeCollider2D BatCollider;
public GameObject Ball;
void Start () {
}
void Update()
{
if (Input.GetKey (KeyCode.Space)) {
kickForce = 1000;
} else {
kickForce = 0;
}
}
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Enemy") // Do not forget assign tag to the field
{
Ball.GetComponent<Rigidbody2D>().AddForce(transform.right * kickForce);
}
}
}

Categories