There is a problem with my bullet firing logic.
The first time everything works fine, but when I shoot the bullet after respawning it doesn't move. It only works for the first shot.
The bullet is a prefab.
Here is my code:
public Rigidbody2D bulletrb;
private float dirX=1;
public float speed=.001f;
public Transform playerPos;
private Vector3 bulletPos;
public GameObject bulletObj;
void Update () {
bulletPos.x = playerPos.transform.position.x + 2;
bulletPos.y = playerPos.transform.position.y + 1;
if (Input.GetKeyDown("1"))
{
bulletrb.velocity = new Vector2(dirX * speed, bulletrb.velocity.y);
Debug.Log("Shoot!");
}
}
private void OnTriggerEnter2D(Collider2D target)
{
if(target.gameObject.tag=="Zombie")
{
Destroy(bulletObj);
Debug.Log("Hited!");
Instantiate(bulletObj,bulletPos,Quaternion.identity,playerPos);
}
}
When you call Destroy, you are losing your active bullet GameObject, but when you call Instantiate, it is not actually assigned back to your bulletObj reference.
The quick and simple fix is:
bulletObj = Instantiate(bulletObj, bulletPos, Quaternion.identity, playerPos);
There are probably more improvements that could be made, but this is the gist of the issue.
One recommendation I would make is using bulletObj.SetActive(false) and bulletObj.SetActive(true) instead of Destroy and Instantiate
Related
new to all this. I've tried following a few examples i've found on here but none seem to work. The best I have right now is
rb.transform.up = rb.GetComponent<Rigidbody2D>().velocity.normalized;
but this makes the rigid body rotate immediately to the new direction of travel. is there a way to make it so it rotates slower rather than jumping in one frame to the new direction of travel?
Any help would be appreciated :)
here is the code ive used to apply the force, if that matters? i got it from a tutorial to apply force based on dragging the mouse
public class DragNShoot : MonoBehaviour
{
public float power = 10f;
public Rigidbody2D rb;
public Vector2 minPower;
public Vector2 maxPower;
public TrajectoryLine tl;
Camera cam;
Vector2 force;
Vector3 startPoint;
Vector3 endPoint;
public void Start()
{
cam = Camera.main;
tl = GetComponent<TrajectoryLine>();
}
public void Update()
{
if (Input.GetMouseButtonDown(0))
{
startPoint = cam.ScreenToWorldPoint(Input.mousePosition);
startPoint.z = 15;
}
if (Input.GetMouseButton(0))
{
Vector3 currentPoint = cam.ScreenToWorldPoint(Input.mousePosition);
currentPoint.z = 15;
tl.RenderLine(startPoint, currentPoint);
}
if (Input.GetMouseButtonUp(0))
{
endPoint = cam.ScreenToWorldPoint(Input.mousePosition);
endPoint.z = 15;
force = new Vector2(Mathf.Clamp(startPoint.x - endPoint.x, minPower.x, maxPower.x), Mathf.Clamp(startPoint.y - endPoint.y, minPower.y, maxPower.y));
rb.AddForce(force * power, ForceMode2D.Impulse);
tl.EndLine();
}
}
}
and here is script for the rotation
public class FaceDirectionOfTravel : MonoBehaviour
{
public Rigidbody2D rb;
// Update is called once per frame
void Update()
{
rb.transform.up = rb.GetComponent<Rigidbody2D>().velocity.normalized;
}
}
As you can see, I have just taken the velocity and applied that to the rotation, I guess this just immediatly changes it to match, but I want it to visibly rotate to match the rotation. I have tried some examples I have seen on here for Slerp but that only seemed to leave it in free rotation.. I must be missing something really obvious so thought I would ask on here. Thanks.
EDIT:
So I've kinda worked out a way to get it to work by creating another object on top of the object i wish to rotate and use Slerp to rotate the object on top to slowly rotate to the same as the original object which has to snap immediatly due to force applied the code is simply
public class RotateSprite : MonoBehaviour
{
[SerializeField] private Rigidbody2D rbOfTarget;
[SerializeField] private Rigidbody2D rb;
private void Start()
{
}
// Update is called once per frame
void Update()
{
rb.transform.position = rbOfTarget.transform.position;
rb.transform.rotation = Quaternion.Slerp(rb.transform.rotation, rbOfTarget.transform.rotation, 30* Time.deltaTime);
}
}
if anyone knows a better solution do let me know. Thanks.
Is your game in 3D or 2D? you keep saying it's 2D but the tag says it is 3D and if 2D you wouldn't need a Z position. and if it is in 3D you would have to change the rigidbody2D to just rigidbody.
I just start making a game with Unity and Visual Studio, i have a bit confuse that why my Speed value always at zero even i set it equal to another variable.
public class Ballista : MonoBehaviour
{
public Transform firePoint;
public GameObject arrowPrefab;
public float speed = 10f;
public float startTimeHold;
public float holdDownTime;
public float arrowSpeed = 100f;
public float finalSpeed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
startTimeHold = Time.time;
}
if (Input.GetKeyUp(KeyCode.M))
{
holdDownTime = Time.time - startTimeHold;
Shoot();
finalSpeed = holdDownTime * arrowSpeed;
}
Debug.Log("holddowntimeis: " + holdDownTime);
Debug.Log("final speed is: " + finalSpeed);
}
public void Shoot()
{
GameObject arrowGO = (GameObject)Instantiate(arrowPrefab, firePoint.position, firePoint.rotation);
}
}
and this is the script that my speed value always zero
{
Rigidbody2D rgBody2D;
public GameObject ballista;
private float speed;
private void Awake()
{
rgBody2D = GetComponent<Rigidbody2D>();
}
private void Start()
{
\\ i though the code below will set my speed equal to my finalSpeed but it still 0
speed = ballista.GetComponent<Ballista>().finalSpeed;
Vector2 mousePos = Input.mousePosition;
Debug.Log("speed: " + speed);
rgBody2D.AddForce(mousePos * speed * Time.deltaTime);
}
void Update()
{
}
}
Different to what others said here you actually do NOT want to do it in Update.
Your goal here is to once give your newly spawned arrow a start velocity, not a continuous force.
The issue here is of other nature I think:
You are always spawning a new instance of your second script from a given prefab. This prefab seems to hold a reference to a Ballista prefab instance. At least you are never assigning a new value to the ballista! It might simply be a wrong reference where the finalSpeed is never updated.
Your are first doing the Shoot and after it set the finalSpeed -> even if it would be the correct reference you always get the wrong finalSpeed value!
I would actually change your two scripts in order toake your arrow instance being controlled by the Ballista instead of letting each spawned arrow poll the speed itself:
public class Ballista : MonoBehaviour
{
public Transform firePoint;
// By giving it the correct type here you don't need GetComponent later
public Rigidbody2D arrowPrefab;
public float startTimeHold;
public float arrowSpeed = 100f;
// I personally would add aax value and clamp since to fast RigidBodies can break collision detection eventually
public float maxArrowSpeed = 300;
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
startTimeHold = Time.time;
}
if (Input.GetKeyUp(KeyCode.M))
{
var holdDownTime = Time.time - startTimeHold;
// get the speed before you shoot
// Here personally I would clamp to be sure the arrow is never faster than maxArrowSpeed
// regardless of how long the user held the button pressed
var finalSpeed = Mathf.Min(holdDownTime * arrowSpeed, maxArrowSpeed);
Debug.Log("holddowntimeis: " + holdDownTime);
Debug.Log("final speed is: " + finalSpeed);
// pass in your speed
Shoot(finalSpeed);
}
}
private void Shoot(float speed)
{
// Instantiate anyway returns the type of the given prefab
// which now is a Rigidbody2D
var arrow = Instantiate(arrowPrefab, firePoint.position, firePoint.rotation);
// Directly set the speed from here
// -> your arrow doesn't even need an extra component
// Since you already spawned it with the correct rotation you maybe don't even need the mouse position thing
// AddForceRelative adds a force in the local space of the arrow so if the rotation is correctly
// this simply adds the force in its forward direction
// Note that also using Time.deltaTime actually only makes sense if you set something continuously
// For a one-time force you wouldn't need it, rather adjust your arrowSpeed field
arrow.AddForceRelative(Vector2.forward * speed);
}
}
Instead of using AddForce or AddForceRelative you could actually also simply set the target velocity:
arrow.velocity = Vector2.forward * speed;
Since you are not updateding it continuously this is totally fine and a lot easier to foresee the actual target speed since when adding a force you have to take the mass and frictions into account. You would than ofcourse have to adjust the arrowSpeed (and evtl maxArrowSpeed) accordingly to not anymore represent a force but an actual velocity in Units/second.
I hope I made my points clear enough, don't hesitate to ask if something stayed unclear ;)
speed = ballista.GetComponent().finalSpeed;
should Come in
void Update(){
}
block and not in
void Start() {
}
as void start is only run once and at that point in time the speed is zero
Hope it helped :)
Start happens only once.
Start is called on the frame when a script is enabled just before any
of the Update methods are called the first time.
Disregarding everything else, i am guessing you want it to update the speed every frame in Update
Update is called every frame, if the MonoBehaviour is enabled.
private void Update()
{
\\ i though the code below will set my speed equal to my finalSpeed but it still 0
speed = ballista.GetComponent<Ballista>().finalSpeed;
Vector2 mousePos = Input.mousePosition;
Debug.Log("speed: " + speed);
rgBody2D.AddForce(mousePos * speed * Time.deltaTime);
}
I'm working on a Unity2D platformer which has blocks that break/fall when the player jumps onto them. Once the player makes contact, there is a short delay, then the block begins to slide down and shortly disappears (using SetActive(false)). If the player dies, then all "falling" blocks should respawn and return to their original positions.
I've made a level where there are two of these blocks (two completely independent gameobjects), but the problem is that when the player breaks both and then dies, when the player respawns only one of the two blocks respawns to its original position. (The other is never to be seen again.)
I think this may have something to do with how they are both attached to the same script. However I don't see how because a different gameobject is still specified in each instance of the script for each block.
Here's the code for the script attached to the block gameobjects (called breakingBlock).
public class BreakingBlock : MonoBehaviour
{
Collider2D boxCollider;
public GameObject slidingBlock;
public PlayerController player;
public float delay;
public float X;
public float startY;
public float endY;
public float speed;
void Start() // Initialises the position of the blocks
{
boxCollider = GetComponent<Collider2D>();
boxCollider.enabled = true;
slidingBlock.transform.position = new Vector2(X, startY);
player = FindObjectOfType<PlayerController>();
}
void OnCollisionEnter2D(Collision2D collision)
// Starts sliding coroutine when touched by player
{
StartCoroutine(Slide(slidingBlock, new Vector2(X, startY), new Vector2(X, endY), delay, speed));
}
public IEnumerator Slide(GameObject slidingBlock, Vector2 start, Vector2 end, float delay, float speed)
{
yield return new WaitForSeconds(delay);
while (slidingBlock.transform.position.y != end.y)
{
// Move towards end position over duration given
slidingBlock.transform.position = Vector2.MoveTowards(slidingBlock.transform.position, end, speed * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
boxCollider.enabled = false;
slidingBlock.SetActive(false);
}
public void ResetSlidingBlock() // Called by another script when player dies
{
Debug.Log("Reset block");
slidingBlock.transform.position = new Vector2(X, startY);
slidingBlock.SetActive(true);
boxCollider.enabled = true;
}
}
This is the part of the other script that calls ResetSlidingBlock from the above script breakingBlock when the player dies.
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player")
{
levelManager.RespawnPlayer();
breakingBlock.ResetSlidingBlock();
}
}
Why is it that only one of the blocks is respawning when separate gameobjects are specified in the separate scripts on completely distinct gameobjects?
You have a public GameObject where only one object is assigned. Doesn't matter how many objects you assign this script to, only that GameObject that you refer to will be reset. If you have X number of GameObjects that you want to follow the same properties, you can:
Make a prefab and access the children using transform.getchild() or tags - assign them to GameObjects
ex:
GameObject objs[];
for(int i=0;i<How many u want;i++)
{
objs[i]=GameObject.Find("ParentObj").transform.GetChild(i);
//something similar to this
}
Or Simply Make public references and assign them manually.
Ex:
public GameObject slideblock1;
public GameObject slideblock2;
etc.
I'll keep this brief and a bit short, but I currently have a particle system that seems to not be rendering even though my collision works.
I have trouble understanding other peoples work so I have not been able to find a solution I can understand.
Here is my code:
public float speed;
public Rigidbody rb;
public int health;
private float knockback;
private float knockup;
public ParticleSystem Eparticle; //*** variable for particle system ***
// Use this for initialization
void Start()
{
rb = GetComponent <Rigidbody>();
knockback = 100f;
knockup = 250f;
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "enemy")
{
health = health - 20;
rb.AddRelativeForce(Vector3.back * knockback);
rb.AddRelativeForce(Vector3.up * knockup);
Destroy(col.gameObject);
Instantiate(Eparticle);
}
if (col.gameObject.name == "endgoal")
{
SceneManager.LoadScene("level 1");
}
}
What am I doing wrong with my instantiate(Eparticle) line?
Could someone please talk me through a solution?
Thank You :)
You should invoke the Instantiate method at the position where you want the particle prefab to appear.
You could do something like this...
Instantiate(Eparticle,transform.position,transform.rotation);
Actually, you can also create (instantiate) a GameObject at runtime as follows...
GameObject obj= Instantiate(Eparticle,transform.position,transfrom.rotation) as GameObject;
This way, you have some sort of 'control' over the instantiated gameobject.
For instance, you can destroy the object after using it by calling the Destroy() method.
E.g.:
Destroy(obj,2f);//Destroys the created object after 2 seconds.
Of course, this is not a good way to go about it if you are going to be instantiating and destroying a lot of objects. You should read about Object Pooling for this purpose.
Hi i am currently working on a turret shooting script. My bullet shoots boet does not go in the enemy's direction. something is wrong or missing and i cant figure out what. I have looked up alot of solutions but cant find recent ones or good ones.
EDIT:
my enemy's are instantiated so there is multiple of them. would that be part of the problem? if not do i need to provide more details? im kina new to the site so pls forgive me if i do stuff wrong.
public GameObject Enemy;
public GameObject Bullet;
public float bulletForce = 100f;
private Vector3 direction;
// Use this for initialization
void Start ()
{
ShootFunctionRepeat();
}
// Update is called once per frame
void Update ()
{
direction = Enemy.transform.position - this.transform.position;
}
void ShootFunctionRepeat()
{
InvokeRepeating("ShootFunction", 0.0f, 1.0f);
}
void ShootFunction(GameObject Bullet)
{
GameObject temp = Instantiate(Bullet, this.transform.position + direction.normalized, Quaternion.identity);
temp.GetComponent<Rigidbody>().AddForce(direction.normalized * bulletForce);
}
Your code won't work. You need to remove the GameObject Bullet paramter from ShootFunction() because the paramter hides this.Bullet. And also Unity3D will print warning message Trying to Invoke method: Shoot.ShootFunction couldn't be called.: you cannot use InvokeRepeating with method parameters.
Now it works:
I used a bullet prefab assigned to public GameObject Bullet. And public GameObject Enemy was also assigned a Cube GameObject from the inspector.
Full project.
But you still need to think about how to recycle and finally destroy the bullets: In your code, you just Instantiate the bullets, but when will they be destroyed?
It's not easy to know what exactly you are trying to do.
But this will at least fix most issues.
You must always normalize your vectors before they can be used as a
direction. A direction is a vector with a magnitude of 1. If you
simply subtract one location from another you don't get a
normalized vector.
I am not sure if your ShootFunction is
allowed to have a parameter. I don't think so. Access you member
(Bullet) directly.
I also removed an offset in your instantiation location. You might need to add it back in. But I believe it is not necessary.
Sample
public GameObject Enemy;
public GameObject Bullet;
public float bulletForce = 100f;
private Vector3 direction;
// Use this for initialization
void Start ()
{
ShootFunctionRepeat();
}
// Update is called once per frame
void Update ()
{
direction = (Enemy.transform.position - this.transform.position).normalized;
}
void ShootFunctionRepeat()
{
InvokeRepeating("ShootFunction", 0.0f, 1.0f);
}
void ShootFunction()
{
GameObject temp = Instantiate(Bullet, this.transform.position, Quaternion.identity);
temp.GetComponent<Rigidbody>().AddForce(direction.normalized * bulletForce);
}