I am making a 2D platformer with Unity and I have run into a small problem. I have enemies that shoot bullets at the player. However, the bullets are only shooting in one direction regardless of which direction the enemy is facing.
In my Enemy Script I have this:
Instantiate(bullet, spawnPosition.position, Quaternion.identity);
and in my bullet script I have this
rigidbody2D.velocity = new Vector2(bulletSpeed,0);
Please help if you can.
I understand why this is happening, but I can not figure out a solution. To update my question I want to be able to check the enemies direction so that I can change the bullet speed to positive/negative to match the direction. Since there will be multiple enemies of this type I do not know how to do this.
public class bulletScript : MonoBehaviour {
// Use this for initialization
private float bulletSpeed;
GameObject parent;
private Vector3 theScale;
void Start () {
rigidbody2D.velocity = new Vector2(bulletSpeed,0);
}
// Update is called once per frame
void Update () {
// if(transform.localScale.x < 0) bulletSpeed = -100;
// if(transform.localScale.x > 0) bulletSpeed = 100;
}
public void SetEnemy(GameObject obj)
{
parent = obj;
}
Then in HammerScript.cs
public class HammerScript : MonoBehaviour {
public bulletScript bullet;
public Transform spawnPosition;
void FixedUpdate ()
{
instantiate(bullet, spawnPosition.position, Quaternion.identity);
((bulletScript)bullet).SetEnemy(this);
}
}
2 new errors:
1-Assets/Scripts/Level 2/HammerScript.cs(89,64): error CS1502: The best overloaded method match for bulletScript.SetEnemy(UnityEngine.GameObject)' has some invalid arguments
2-Assets/Scripts/Level 2/HammerScript.cs(89,64): error CS1503: Argument#1' cannot convert HammerScript' expression to typeUnityEngine.GameObject'
the bullets are only shooting in one direction regardless of which direction the enemy is facing.
Well, unless bulletSpeed is positive or negative depending on the enemy's direction, every bullet will have the same speed and direction. In your code, the bullets' velocities do not depend on the enemies' directions at all.
What you could do is keep a reference in the bullet to the enemy that it came from, and then set the velocity according to that enemy's info.
You can do that by having a SetEnemy method that takes an Enemy as a parameter in your Bullet class, and then you can just call ((bulletScript)bullet).SetEnemy(this); immediately after the Instantiate call.
So your HammerScript file should look like this:
public class HammerScript : MonoBehaviour
{
public bulletScript bullet;
public Transform spawnPosition;
void FixedUpdate ()
{
instantiate(bullet, spawnPosition.position, Quaternion.identity);
((bulletScript)bullet).SetEnemy(this);
}
}
Then, in your Bullet class you can have the following:
class bulletScript : MonoBehavior
{
GameObject parent;
public void SetEnemy(GameObject obj)
{
parent = e;
}
// ... whatever else you have, including the method that sets the velocity
}
Then in your bullet script you would set rigidbody2D.velocity to something that has to do with the enemy (which would be this.parent).
Your current implementation only allow the bullet to go into one direction. Let's assume your bulletSpeed is always positive.
If you consider another direction with the angle alpha from that base direction, then you need to create a rotated Vector, which you can do like this:
new Vector2(bulletSpeed * Mathf.Cos(alpha), bulletSpeed * Mathf.Sin(alpha));
Also consider that alpha has to be given as radians, if you truly want to use degrees calculate the radians first:
alpha = (degrees / 180) * 90
Related
I want a character that simply walks forward until it hits a wall, then it does a 180 degree spin and repeats the action.
Making him walk forward is easy, how do I program him hitting the wall?
My current code:
public class Enemy : MonoBehaviour
{
public float speed = 5;
public Vector3 userDirection = Vector3.forward;
// Update is called once per frame
void Update()
{
transform.position += userDirection * speed * Time.deltaTime;
}
}
You can use raycast to detect walls and avoid them.
Raycast in Unity is a Physics function that projects a Ray into the scene, returning a boolean value if a target was successfully hit
The following code is a simple demenstration of raycast. Distance determine how far you ray is casted and layermask determine which layers ray should detect. Therefore, you must put your walls in a Layer and sit this variable equal to that:
public float distance;
public LayerMask wallLayerMask;
public Vector3 userDirection = Vector3.forward;
void Update()
{
if (Physics.Raycast(transform.position, userDirection, distance, wallLayerMask))
{
// rotate, possibly:
// userDirection *= -1
}
transform.position += userDirection * speed * Time.deltaTime;
}
UPDATE
As stated in comments, you can also use colliders. To use colliders, you need to add another empty gameObject to your enemy, add a SphereCollider to it. Sphere Collider's radius determines how far you want to detect walls. Then add the following code to the second object:
// a reference to your enemy controller class
public Enemy enemy;
private void OnCollisionEnter(Collider other)
{
enemy.Rotate();
}
The OnCollisionEnter is called whenever an object, which has a collider, collides with our object. You need to assign a layer to this new object (wallCheck) and another to your walls. Then from Edit -> Project Settings -> Physics uncheck the collision of wallCheck with any layer other than your walls layer.
I have a fps controller structured like this:
Whenever I press e on a pickup weapon prefab, I want to move the weapon to a predefined offset and be the parent of the "Weapons" gameobject under camera.
I have structured the project like this:
The weapon pickup prefab has a pickup script that looks like this:
public class WeaponPickup : MonoBehaviour
{
[SerializeField] private Weapon weapon;
public void PickUpWeapon(Fighter fighter)
{
fighter.EquipWeapon(weapon, transform.position, transform.rotation);
Destroy(gameObject);
}
}
The Fighter class that is located on the player has the EquipWeapon method that is called from the pickup script
public void EquipWeapon(Weapon weapon, Vector3 pickupPosition, Quaternion pickupRotation)
{
if (currentWeapon != null)
{
currentWeapon.weaponGO.SetActive(false);
}
currentWeapon = weapon;
weaponList.Add(currentWeapon);
currentWeaponIndex = weaponList.Count - 1;
currentWeapon.Spawn(pickupPosition, weaponsParent, this);
}
I have a scriptable object class that is called Weapon, which has the Spawn method implemented in it:
public void Spawn(Vector3 pickupPosition, Transform weaponParent, Fighter fighter)
{
if (weaponPrefab != null)
{
weaponGO = Instantiate(weaponPrefab);
fighter.MoveToPos(pickupPosition, weaponParent.TransformPoint(weaponPosition), 1f, weaponGO.transform, weaponParent);
}
}
--
The fighter.MoveToPos calls a corutine that looks like this:
public void MoveToPos(Vector3 startPosition, Vector3 endPosition, float duration, Transform goTransform, Transform weaponParent)
{
StartCoroutine(LerpPosition(startPosition, endPosition, duration, goTransform, weaponParent));
}
private IEnumerator LerpPosition(Vector3 startPosition, Vector3 endPosition, float duration, Transform goTransform, Transform weaponParent)
{
float time = 0;
goTransform.position = startPosition;
while (time < duration)
{
goTransform.position = Vector3.Lerp(startPosition, endPosition, time / duration);
time += Time.deltaTime;
yield return null;
}
goTransform.position = endPosition;
goTransform.parent = weaponParent.transform;
}
And the question is when I start the game and pick up the weapon, the weapon appears to lerp from the pickup position to the correct position but then it teleports to a random position.
Other info:
WeaponGo in the Weapon class is the refrence to the weapon's gameobject.
The lerprotation of the object is not yet implemented.
I tried converting every position / Vector3 to world / localPosition and then apply the parenting to the "Weapons" gameobject and it didn't work.
My guess it's that I am not changing correctly the localPosition / world position respectively as the object becomes parented at the end of the lerping.
If you need any other info, please don't hesitate to ask. Any suggestions are greatly appreaciated.
I actually found out what the problem was, I should have used Transform.InverseTransformPoint insted of Transform.TransformPoint.
Huh. At first glance your code looks like it should do what you want. I'd recommend adding some Debug.Log lines to track the local and global positions before and after the parent assignment, out of curiosity. It does seem like it's a local/global positioning issue.
You could try using:
goTransform.SetParent(weaponParent.transform, false);
instead of:
goTransform.parent = weaponParent.transform;
The 'false' bool in SetParent refers is for the 'worldPositionStays' parameter, which seems like it should do the opposite of what your desired result, but since the opposite of your desired result is already happening, maybe that will work?
I'm doing a simple game to get started with unity, there's a cube who goes straight and dodge other cubes (obstacles) which are spawned randomly. To make the obstacle always spawn in front of the player I set up a code that makes the 7 spawn position (the blocks get generated in them randomly) follow the player's coordinates but adding 100 to the Z so they get generated not upside the player. Now, my problem is that when generated the obstacle also changes their X position, making them fall from the platform when I go to the right or to the left with the player. How can I make they follow only the Z position of the player and not the X?
Here is the code that makes the spawners change position:
public class MoveSpawn : MonoBehaviour
{
public Transform player;
public Vector3 offset;
// Update is called once per frame
void Update()
{
transform.position = player.position + offset;
}
}
I would simply multiply the player's position by (0,0,1) (in other words, Vector3.forward) before adding it to the offset:
public class MoveSpawn : MonoBehaviour
{
public Transform player;
public Vector3 offset;
// Update is called once per frame
void Update()
{
transform.position = player.position * Vector3.forward + offset;
}
}
Oh, that's pretty easy.
So if you only want to look at the Z(or any other) coordinates of the Cube or any object in Unity you can simply use:
transform.position.z
That's the same thing you would do with vectors. Something like that:
Vector3 offset = ...;
Debug.Log(offset.x) //returns x coordinate of offset
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);
}
I'm trying to develop a simple shooting mechanism which instantiates a 'shot' based on the angle of rotation of the shooter. My problem here is even though the shooter is oriented at 0*, the shot fires at an angle of 45*(this is what I'm guessing the problem is because when the shooter is oriented at 45*, the shot is fired at exact 90*).
Shooter angle(0,0,0)
Shooter angle(0,0,45)
Note- The ball always launches from the center of the black flattened cylinder object.
Required Code:
public class ShotMoveScript : MonoBehaviour {
public static float xForce;
public Transform shott;
void Update () {
if(Input.GetKey(KeyCode.Q))
{
transform.Rotate(Vector3.forward, 5f);
}
if(Input.GetKey(KeyCode.E))
{
transform.Rotate(Vector3.forward, -5f);
}
if(Input.GetKey(KeyCode.Space))
{
xForce += 0.2f;
}
if(Input.GetKeyUp(KeyCode.Space))
{
Instantiate(shott, transform.position, transform.rotation);
}
}
}
Script attached to the ball which gets instantiated:
public class MovementScript : MonoBehaviour {
void Update () {
Rigidbody2D rb;
rb = GetComponent<Rigidbody2D> ();
rb.gravityScale = 0.8f;
transform.Translate( new Vector3(1,1,0) * ShotMoveScript.xForce * Time.deltaTime, Space.Self);
}
}
You rotate the ball when instantiating it with the third parameter rotation (see reference).
After that you apply a Vector pointing to (1, 1) so the ball will move along that direction relative to his local rotation.
Either pass Quaternion.identity as the third parameter to Instantiate method or move the ball along (1, 0) direction.