using UnityEngine;
using System.Collections;
using System;
public class TurretScript : MonoBehaviour
{
public float rangeToPlayer;
public GameObject bullet;
// public GameObject spherePrefab;
private GameObject player; // Tag for DynamicWaypointSeek
private bool firing = false; //Firing status
private float fireTime; // Fire Time
private float coolDown = 1.00F; // Time to cool down fire
private int health = 5; //Health of turret
private bool bDead;
private Action cur;
void Start()
{
player = GameObject.FindWithTag("Player");
bDead = false;
}
void Update()
{
if (PlayerInRange())
{ // PlayerInRange is bool function defined at the end
transform.LookAt(player.transform.position); //Rotates the transform (weapon) so the forward vector points at /target (Player)/'s current position
RaycastHit hit;
if (Physics.SphereCast(transform.position, 0.5F, transform.TransformDirection(Vector3.forward), out hit))
{ //The center of the sphere at the start of the sweep,The radius of the sphere,The direction into which to sweep the sphere.
if (hit.transform.gameObject.tag == "Player")
{ //information in hit (only interested in "Player")
if (firing == false)
{
firing = true;
fireTime = Time.time; //keep the current time
GameObject bul;
Quaternion leadRot = Quaternion.LookRotation(player.transform.position); //Calculate the angular direction of "Player";
bul = Instantiate(bullet, transform.position, leadRot) as GameObject; // existing object to be copied, Position of Copy, Orientation of Copy
//Destroy(bullet, 2.0f);
}
}
}
}
if (firing && fireTime + coolDown <= Time.time) // prvious time stored in fireTime + cool down time is less --> means the firing must be turn off
firing = false;
// Destroy(GameObject.FindGameObjectWithTag("Bullet"));
if (health <= 0)
cur = Deadstate;
}
protected void Deadstate()
{
if (!bDead)
{
bDead = true;
Explode();
}
}
void Explode()
{
Destroy(gameObject, 1.5f);
}
bool PlayerInRange()
{
return (Vector3.Distance(player.transform.position, transform.position) <= rangeToPlayer); //Vector3.Distance(a,b) is the same as (a-b).magnitude.
}
void OnTriggerEnter(Collider col)
{
HealthBarScript.health -= 10f;
}
}`
This is code I wrote for a enemy turret in my current unity project. The idea is that it will fire a bullet at the player once it's in range and stop when it's out of range and when the bullet collides with the player, the player will take damage, but I'm struggling to figure out why the bullet won't do any damage. Thoughts? Very much appreciate the help!
According to my experience: bullet speed may be too fast to detect collision.
Imagine that in 2d:
frame 1:
.BB.......OOO......
frame 2:
........BBOOO......
frame 3:
..........OOO..BB..
Where OOO is your object and BB is your bullet.
To fix this you can have a bigger collider for the bullet. Other workaround like "dynamic" collider are also possible.
Related
I'm trying to make a 2D top-down shooter in Unity. I want it so when the you hold down the left mouse button the player fires a series of bullets until you run out of ammo. The player's movement speed is slowed while firing and the players movement speed should be added to the bullet's movement speed. For some reason the players movement speed is only applied to the bullets AFTER the first bullet is fired. The first bullet always seems to keep the slightly faster 'sprint' movement speed.
Weapon script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour
{
private GameObject bulletPrefab;
private Transform firePoint;
private PlayerControls player;
public float fireForce = 10f;
private bool cooldown = false;
private int bullets;
public int bulletDamage;
public int maxAmmo;
public float fireRate = 0.5f;
public float reloadRate = 2.5f;
public bool noAmmo = false;
public float walkSpeed = 2f;
private float timeSinceLastShot = 0f;
void Update()
{
// increase time since last shot
timeSinceLastShot += Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
player.moveSpeed = walkSpeed;
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
GameObject bullet = Instantiate(bulletPrefab, firePoint.transform.position, Quaternion.identity);
bullet.GetComponent<Bullet>().bulletDamage = bulletDamage;
// add player movement speed to bullet's speed
bullet.GetComponent<Rigidbody2D>().velocity = player.GetComponent<Rigidbody2D>().velocity;
bullet.GetComponent<Rigidbody2D>().AddForce(transform.up * fireForce, ForceMode2D.Impulse);
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
else
{
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
}
}
public void FillMag()
{
bullets = maxAmmo;
noAmmo = false;
}
}
PlayerControls:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControls : MonoBehaviour
{
public float moveSpeed = 5f;
public float baseSpeed = 5f;
public int health;
public Weapon weapon;
private Rigidbody2D rb;
Vector2 mousePosition;
Vector2 moveDirection;
public float walkSpeed = 2f;
void Update()
{
float moveX = Input.GetAxisRaw("Horizontal");
float moveY = Input.GetAxisRaw("Vertical");
moveDirection = new Vector2(moveX, moveY).normalized;
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
rb.velocity = new Vector2(moveDirection.x * moveSpeed, moveDirection.y * moveSpeed);
Vector2 aimDirection = mousePosition - rb.position;
float aimAngle = Mathf.Atan2(aimDirection.y, aimDirection.x) * Mathf.Rad2Deg - 90f;
rb.rotation = aimAngle;
}
}
The player is moving from left to right.
Player firing
According to Unity's lifecycle, inputs are only calculated just before the Update method is called, but physics are applied during the FixedUpdate method. Is this what is causing my problems? I've tried moving some calculations into FixedUpdate and LateUpdate but nothing seems to make any difference.
Any help is appreciated. I've been banging my head against this for a few days now. I'm an amature, so feel free to explain like I'm 5.
So there are 2 problems with your code:
if you are not holding down left mouse, but multiple click instead:
the else statement in the Update() will immediately set it player back to base speed, which is not really matter if you intent to holding your mouse.
There are 2 concurrent things happen
first, you set player moveSpeed in Weapon Update()
at the same time, you calculate player velocity in Player Update()
But it TAKES TIME for player to update velocity before you get it right to your weapon.
Therefore, I recommend you to use IEnumerator to delay the fire action.
public class Weapon : MonoBehaviour
{
void FixedUpdate()
{
// increase time since last shot
timeSinceLastShot += Time.deltaTime;
// if left-click is held down
if (Input.GetMouseButton(0))
{
// if enough time has passed since last shot
if (timeSinceLastShot >= fireRate && noAmmo == false)
{
//player.moveSpeed = walkSpeed;
StopAllCoroutines();
StartCoroutine(SetSpeed());
bullets -= 1;
cooldown = true;
if (bullets <= 0)
{
noAmmo = true;
player.moveSpeed = player.baseSpeed;
}
// instantiate a bullet
StartCoroutine(FireBulelt());
// reset time since last shot
timeSinceLastShot = 0f;
}
}
// if left-click is not held down
}
IEnumerator SetSpeed()
{
player.moveSpeed = walkSpeed;
yield return new WaitForSeconds(0.5f);
cooldown = false;
// restore player movement speed
player.moveSpeed = player.baseSpeed;
yield return null;
}
IEnumerator FireBulelt()
{
yield return new WaitForSeconds(0.05f);
GameObject bullet = Instantiate(bulletPrefab, player.transform.position, Quaternion.identity);
print(" player.GetComponent<Rigidbody2D>().velocit " + player.GetComponent<Rigidbody2D>().velocity);
// add player movement speed to bullet's speed
Rigidbody2D bulletRB = bullet.GetComponent<Rigidbody2D>();
print(" bulletRB.velocit " + bulletRB.velocity);
bulletRB.velocity = player.GetComponent<Rigidbody2D>().velocity;
print(" after bulletRB.velocit " + bulletRB.velocity);
bulletRB.AddForce(transform.up * fireForce, ForceMode2D.Impulse);
}
}
And you should add print() like I did to keep track of code behavior when you're debugging.
This is my shooting script, I set these in fixed update method, as it should be, but my mouse inputs keeps pressed, I am trying to make an fps game but my mouse input keeps pressed, anyone can help me? This also happens in keyboard inputs.
void FixedUpdate()
{
if(isReloading)
{
return;
}
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
if(Input.GetKey(KeyCode.R))
{
Debug.Log("R key was pressed.");
StartCoroutine(Reload());
return;
}
if(Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
{
nextTimeToFire = Time.time + 1f / fireRate;
Shooting();
}
}
IEnumerator Reload()
{
isReloading = true;
UIController.instance.reloadMSG.gameObject.SetActive(true);
Debug.Log("reloading");
yield return new WaitForSeconds(reloadTime);
currentAmmo = maxAmmo;
isReloading = false;
UIController.instance.reloadMSG.gameObject.SetActive(false);
}
private void Shooting()
{
currentAmmo--;
Debug.Log("Current Ammo:" + currentAmmo);
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
GameObject bulletImpactObject = Instantiate(bulletImpact, hit.point + (hit.normal * 0.002f), Quaternion.LookRotation(hit.normal, Vector3.up));
Destroy(bulletImpactObject, 10f);
}
UIController.instance.ammoTXT.text = (currentAmmo + " / " + maxAmmo).ToString();
}
A few years ago I did a top-down shooter, and I had a similar problem. Try changing this:
if(Input.GetButton("Fire1") && Time.time > nextTimeToFire)
{
nextTimeToFire = Time.time + fireRate;
Shooting();
}
Removing the '=' from the comparison, and just adding the fire rate instead of dividing. That's how I did back then.
Also, are you planning to do something while the gun is reloading? Because if not and it's just checking if it's reloading, you can just do this:
if !(isReloading)
{
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
if(Input.GetKey(KeyCode.R))
{
Debug.Log("R key was pressed.");
StartCoroutine(Reload());
return;
}
if(Input.GetButton("Fire1") && Time.time > nextTimeToFire)
{
nextTimeToFire = Time.time + fireRate;
Shooting();
}
}
Here is GunController script that does what you want. Added is a weapon recoil
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunController : MonoBehaviour
{
public GameObject bulletPrefab; // Prefab of the bullet
public Transform bulletSpawn; // Spawn point for the bullet
public float fireRate = 0.5f; // Rate of fire in seconds
public float recoil = 10f; // Recoil force applied to the player
public int magazineSize = 6; // Size of the magazine
public float reloadTime = 1.5f; // Time it takes to reload the gun
private int currentAmmo; // Current ammo in the magazine
private bool isReloading; // Flag to check if the gun is being reloaded
private float nextFire = 0.0f; // Time when the player can fire again
void Start()
{
// Initialize the current ammo to the size of the magazine
currentAmmo = magazineSize;
}
void Update()
{
// Check if the player has pressed the fire button and if it's time to fire again
if (Input.GetButton("Fire1") && Time.time > nextFire && !isReloading && currentAmmo > 0)
{
// Set the time when the player can fire again
nextFire = Time.time + fireRate;
// Create a bullet at the spawn point
var bullet = (GameObject)Instantiate(bulletPrefab, bulletSpawn.position, bulletSpawn.rotation);
// Add force to the bullet
bullet.GetComponent<Rigidbody>().AddForce(bullet.transform.forward * 500);
// Apply recoil force to the player
GetComponent<Rigidbody>().AddForce(-transform.forward * recoil, ForceMode.Impulse);
// Decrement the current ammo
currentAmmo--;
}
// Check if the player has pressed the reload button and if the gun is not being reloaded
if (Input.GetKeyDown(KeyCode.R) && !isReloading)
{
// Set the reload flag to true
isReloading = true;
// Start the reload coroutine
StartCoroutine(Reload());
}
}
IEnumerator Reload()
{
// Wait for the reload time
yield return new WaitForSeconds(reloadTime);
// Set the current ammo to the size of the magazine
currentAmmo = magazineSize;
// Set the reload flag to false
isReloading = false;
}
}
This script should be attached to the game object that represents the gun. The script has several public variables that you can customize:
bulletPrefab is the prefab of the bullet that will be fired.
bulletSpawn is the transform component of the game object that represents the spawn point for the bullet.
fireRate is the rate of fire in seconds. This determines how often the gun can be fired.
recoil is the recoil force applied to the player when the gun is fired.
magazineSize is the size of the magazine. This determines how many bullets the gun can hold.
So, I am trying to create a soccer game from scratch... all I have done until now, is setting up the ball. This is how I want it to work: When the player collides with the ball, the ball jumps forward a bit. If you start running the ball will be pushed further away.
Now, here is my script for the ball (I am using the standard FPSController as character):
using UnityEngine;
using System.Collections;
public class BallController : MonoBehaviour {
private Rigidbody rb;
public GameObject character;
public float moveSpeed = 1000;
public float shootSpeed = 2000;
bool isTurnedUp = false;
bool isTurnedDown = false;
bool done = false;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate () {
//Debug.Log(isTurnedUp + ", " + isTurnedDown);
switch (character.GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>().m_IsWalking)
{
case true:
if (isTurnedUp == false)
{
moveSpeed = moveSpeed / 1.4f;
isTurnedUp = true;
isTurnedDown = false;
}
break;
case false:
if (isTurnedDown == false)
{
moveSpeed = moveSpeed * 1.4f;
isTurnedDown = true;
isTurnedUp = false;
}
break;
}
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Vector3.Distance(gameObject.transform.position, character.transform.position) <= 5)
{
float distance = Vector3.Distance(gameObject.transform.position, character.transform.position);
}
}
}
void OnCollisionEnter(Collision collision) {
FixedUpdate();
if (done == false) {
rb.AddForce(Vector3.forward * moveSpeed, ForceMode.Impulse);
done = true;
}
else {
done = false;
}
}
//other
void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, 2);
}
}
My problem is that the ball doesn't behave how I want it... it feels like it's about luck if the ball will jump forward when I touch it. Can someone tell me what I did wrong?
Inside of OnCollisionEnter you need to ensure the ball can only be kicked by the player. You can check whether or not the player has collided with the ball by checking the name or tag of the collision. The following example uses the name and assumes your player GameObject is named "Player".
Remove the done flag since this will only allow the player to kick the ball every other time they collide, and remove the FixedUpdate() call since FixedUpdate() is already called automatically every physics calculation.
Finally, if you want to kick the ball away from the player, then you need to calculate the direction away from the collision point instead of using Vector3.forward as seen below.
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.name == "Player")
{
Vector3 direction = (collision.transform.position - transform.position).normalized;
rb.AddForce(-direction * moveSpeed, ForceMode.Impulse);
}
}
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);
}
}
}
I'm trying to make a spring wall in my 2d platformer. However every way I've tried to move my character (addforce, transform.translate and even tried using the bouncy naterial) teleports my character rather then moving it. This doesn't happen with my controller script. I suspect something in my script is causing this interaction but I'm not sure exactly what. Here is my controller script. Any suggestions would be greatly appreciated :))
using UnityEngine;
using System.Collections;
public class controller : MonoBehaviour
{
//how fast he can go
public float topSpeed = 15f;
bool facingRight = true;
//what direction character is facing
bool grounded = false;
//check if the character is grounded
public Transform groundCheck;
//the transform is used to see if character has touched the ground yet
float groundRadius = 0.2f;
//creates a ground radius for the transform circle for ground detection
GameObject Player, Player2;
int characterselect;
//I'm pretty sure this stuff ^^ is unnessecary and is from when I had the character switch in this script but dont want to remove just in case
public float jumpForce = 700f;
//the characters jumpforce
public GameObject jumpParticles;
public LayerMask whatIsGround;
//what layer is the ground
void Start()
{
characterselect = 1;
Player = GameObject.Find("Player");
Player2 = GameObject.Find("Player2");
//loads game objects as variables I'm pretty sure this stuff ^^^^ is unnessecary and is from when I had the character switch in this script but dont want to remove just in case
}
void FixedUpdate()
{
//has the transform hit the ground yet returns a true or false value
grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
// get direction
float move = Input.GetAxis("Horizontal");
//add velocity to the move direction times by the speed
GetComponent<Rigidbody2D>().velocity = new Vector2(move * topSpeed, GetComponent<Rigidbody2D>().velocity.y);
if (move > 0 && !facingRight) //if facing not right then use the flip function
flip();
else if (move < 0 && facingRight)
flip();
//the whole flip turned out not to be nesseary as my sprites were symetric
}
void Update()
{
//if the character is in fact touching the ground then when space is pressed the function will run
if(grounded&& Input.GetKeyDown(KeyCode.Space))
{
//adds the jump force to the rigidbody attached to the character
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));
//Instantiate(jumpParticles, transform.position, transform.rotation);
//Destroy(jumpParticles, 3f);
}
{
//this was code I was working on for character switching but couldn't get it to work properly. I didn't delete it because it took me ages to do and was recycled in the characterswitch script
// if (Input.GetKeyDown(KeyCode.E))
// {
// if (characterselect==1)
// {
// characterselect = 2;
// }
// else if (characterselect==2)
// {
// characterselect = 1;
// }
// }
// if (characterselect==1)
// {
// Player.SetActive(true);
// Player2.SetActive(false);
// }
// else if (characterselect==2)
// {
// Player.SetActive(false);
// Player2.SetActive(true);
// }
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
topSpeed = topSpeed * 2;
}
else if (Input.GetKeyUp(KeyCode.LeftShift))
{
topSpeed = 15;
}
}
void flip()
{
//for when facing the other direction
facingRight = ! facingRight;
//load the local scale
Vector3 theScale = transform.localScale;
//flip character on the x axis
theScale.x *= -1;
//and then apply it to the local scale
transform.localScale = theScale;
}
Edit
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class boost : MonoBehaviour {
private Rigidbody2D rb2d;
// Use this for initialization
void OnCollisionEnter2D(Collision2D other){
if (other.gameObject.tag == "booster") {
Vector2 tempvect = new Vector2 (2, 0);
rb2d.MovePosition ((Vector2)transform.position + tempvect);
}
}
void Start () {
rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
}
}
This is the code that I think should make it work the error comes at
rb2d.MovePosition ((Vector2)transform.position + tempvect);