Split a bullet in 3 different directions - c#

I'm trying to assign a skill to a projectile that, when used, divides the project into 3 (the original projectile and 2 more new ones).
However, when I instantiate these two clones, I cannot attribute any movement to them. The idea would be for them to take this route:
The green dotted curve indicating the motion of the original bullet, the blue vector indicating the instantaneous velocity of the original bullet at time of special activation, the red vectors indicating the two velocity vectors belonging to each of the newly spawned bullets, and the green angle indicating the direction of the new bullet relative to the original velocity direction
But at the moment, they are just standing at the point of the parabola where they were instantiated. No matter what I do, I can't seem to attach any value to their rigidbody2D. Does anyone know how to fix this?
This is my code so far
Ability Script:
public class AirSpecialSplit : MonoBehaviour, IAirSpecial
{
public float SplitAngleInDegrees = 10;
GameObject bird_down;
GameObject bird_up;
public void ExecuteAirSpecial()
{
{
//hold the velocity of the original bird
Vector2 original_velocity = this.gameObject.GetComponent<Rigidbody2D>().velocity;
//clone two new birds
bird_down = Birb.MakeBirbCopy(this.gameObject);
bird_up = Birb.MakeBirbCopy(this.gameObject);
//get the rigidboy from the clones
Rigidbody2D rb_bird_down = bird_down.GetComponent<Rigidbody2D>();
Rigidbody2D rb_bird_up = bird_up.GetComponent<Rigidbody2D>();
rb_bird_down.velocity = new Vector2(original_velocity.x, original_velocity.y) * Time.deltaTime;
rb_bird_up.AddForce(new Vector2(3, 5) * 500);
}
}
}
Main Bird:
public class Birb : MonoBehaviour
{
#region Provided Code, Do Not Edit
private Rigidbody2D m_rigidbody;
private bool m_canExecuteAirSpecial = true;
public bool CanExecuteAirSpecial
{
get
{
return m_rigidbody.simulated && m_canExecuteAirSpecial;
}
}
private void Awake()
{
m_rigidbody = GetComponent<Rigidbody2D>();
StopBirbSimulation();
}
public void StopBirbSimulation()
{
m_rigidbody.simulated = false;
}
public void StartBirbSimulation()
{
m_rigidbody.simulated = true;
}
public void SetPosition( Vector3 position )
{
if ( m_rigidbody.simulated )
{
m_rigidbody.MovePosition( position );
}
else
{
transform.position = position;
}
}
public void ExecuteAirSpecial()
{
GetComponent<IAirSpecial>().ExecuteAirSpecial();
m_canExecuteAirSpecial = false;
}
private void OnCollisionEnter2D( Collision2D collision )
{
m_canExecuteAirSpecial = false;
}
public static GameObject MakeBirbCopy( GameObject original )
{
Birb newBirb = Instantiate(original).GetComponent<Birb>();
newBirb.m_canExecuteAirSpecial = false;
return newBirb.gameObject;
}
#endregion
[Range( 0, 25 )]
public float LaunchForce = 12;
public void Launch(Vector3 offset, float maximumStretch, Rigidbody2D rigidbody)
{
rigidbody.velocity = new Vector2(offset.x * -LaunchForce, offset.y * -LaunchForce) * (maximumStretch/2);
}
}

Ok, apparently, I just need to set the "simulated" to true after instantiate.
rb_bird_down.simulated = true;
rb_bird_up.simulated = true;
The documentation could be clearer about this since they say that all attributes are copied, which is not the case...

Related

How do I Instantiate a line renderer and give it positions for its points

I apologize for the block of code below but I am not sure where the issue is. I am trying to get a laser to appear off screen and attempt to hit the player. I have no idea why this code does nothing. It is most likey just a stupid mistake I have made.
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lasers : MonoBehaviour
{
private LineRenderer lr;
public bool Above = true;
public bool Below = false;
public bool Left = false;
public bool Right = false;
public Transform LaserStartPoint;
public Transform LaserEndPoint;
public Transform Player;
public float LaserWidth = 0.75f;
public float LaserLength = 19f;
public float LaserDuration = 0.5f;
public float LaserFadeDuration = 0.5f;
public GameObject Laser;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
ShootLaser();
}
}
void ShootLaser()
{
if(Above)
{
LaserStartPoint.position = new Vector3(Random.Range(-10,10), 6, -1);
}
if(Below)
{
LaserStartPoint.position = new Vector3(Random.Range(-10,10), -6, -1);
}
if(Left)
{
LaserStartPoint.position = new Vector3(-10, Random.Range(-6,6), -1);
}
if(Right)
{
LaserStartPoint.position = new Vector3(10, Random.Range(-6,6), -1);
}
Instantiate(Laser, LaserStartPoint.position, Quaternion.identity);
lr = Laser.GetComponent<LineRenderer>();
lr.startWidth = LaserWidth;
lr.endWidth = LaserWidth;
lr.positionCount = 2;
Vector3 dir = Player.position - LaserStartPoint.position;
LaserEndPoint.position = LaserStartPoint.position + dir.normalized * LaserLength;
lr.SetPosition(0, LaserStartPoint.position);
lr.SetPosition(1, LaserEndPoint.position);
Invoke("FadeLaser", LaserDuration);
}
void FadeLaser()
{
lr.startColor = Color.Lerp(lr.startColor, Color.clear, LaserFadeDuration);
lr.endColor = Color.Lerp(lr.endColor, Color.clear, LaserFadeDuration);
Laser.SetActive(false);
}
}
`
I have looked in the Hierarchy and nothing is spawning. None of the values are changing on the Script. I have the script attached to the main camera and it spawns in my LaserLineRenderer prefab.
You are attempting to modify the line renderer component of the prefab. You need to grab the reference of the instantiated object when it is spawned in.
private GameObject instantiatedLazer;
/*In ShootLaser function*/
instantiatedLazer = Instantiate(Laser, LaserStartPoint.position, Quaternion.identity);
lr = instantiatedLazer.GetComponent<LineRenderer>();
/*In FadeLaser function*/
instantiatedLazer.SetActive(false);

Handling automatic weapons in Unity 2D

I am working on simple 2D rouge-like game with weapons, currently my weapon script only handles the shooting of weapon once a left mouse button is clicked. What I am looking for is how to make that shooting happen at some interval. So when a user clicks mouse0 the bullet would shoot but there will be a delay before they can shoot again, as well as if the mouse0 is held then the weapon would shoot at specified firing rate.
Code for my controller:
using UnityEngine;
/*
* This class will be attached to every gun to monitor it's actions on key presses.
* WeaponScript will be called and will be attached to each weapon as well depending on it's type with
* stats. WeaponScript will manage how a weapon behaves instead of this script, this is just a mediator
* so I don't have to write this code in abstract class.
*/
public class WeaponPickUpController : MonoBehaviour
{
//public variables
public float pickUpRange; // range at which a gun will be avaialable to pick up, will probably be constant in the end
public GameObject player = null; // stores player object for it's rb component
public Weapon weaponScript = null;
//protected variables
//private variables
private readonly KeyCode _dropKey = KeyCode.Q; // todo: replace to read from config some day
private readonly KeyCode _pickUpKey = KeyCode.E; // todo: replace to read from config some day
private readonly KeyCode _shootKey = KeyCode.Mouse0; // todo: replace to read from config some day
[SerializeField] private bool _equiped = false; // todo: remove serialize field
// Start is called before the first frame update
void Start()
{
// todo: init variables safely here, also perhaps set variable on save file
}
// Update is called once per frame
void Update()
{
var distanceFromPlayer = Vector2.Distance(player.transform.position, this.transform.position);
//Debug.Log(distanceFromPlayer);
// short circuit this if gun is equiped
if (!_equiped && distanceFromPlayer < pickUpRange && Input.GetKeyDown(_pickUpKey)) // todo: a range check here
{
PickUpWeapon();
}
//short circuit this if it is not equiped
if (_equiped && Input.GetKeyDown(_dropKey))
{
DropWeapon();
}
// short circuit if not equiped
if (_equiped && Input.GetKeyDown(_shootKey))
{
weaponScript.Shoot();
}
}
private void PickUpWeapon()
{
_equiped = true;
weaponScript.GetEquiped();
}
private void DropWeapon()
{
_equiped = false;
weaponScript.GetUnequiped();
}
}
Code for Weapon:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour
{
public float damage = 10f;
public float projectileSpeed = 1f;
public float damageBonus = 0f;
[SerializeField] private Rigidbody2D _rb = null;
[SerializeField] private GameObject _player = null;
[SerializeField] private Transform _firePoint = null;
[SerializeField] private GameObject _projectile = null;
//Only called once
private void Start()
{
// load form config
}
// todo: make abstract since this is a superclass
public void Shoot()
{
GameObject projectile = Instantiate(_projectile, _firePoint.position, _firePoint.rotation); // instantiate projectile and make it a child of weapon
Rigidbody2D projectile_rb = projectile.transform.GetComponent<Rigidbody2D>();
//projectile_rb.AddForce(_firePoint.up * projectileSpeed, ForceMode2D.Impulse);
projectile_rb.velocity = _firePoint.up * projectileSpeed;
}
public void GetEquiped()
{
Transform playerTransform = _player.transform;
float playerLocalOffset = _player.GetComponent<BoxCollider2D>().size.x;
_rb.isKinematic = true; // makes this move with the player
transform.rotation = playerTransform.rotation;
transform.SetParent(playerTransform);
transform.localPosition = new Vector3(-playerLocalOffset, 0, playerTransform.position.z);
}
public void GetUnequiped()
{
_rb.isKinematic = false; // makes it dynamic agian
transform.SetParent(null);
transform.position = new Vector3(transform.position.x, transform.position.y, 2);
}
}
Well in your weapon you could have a cooldown like e.g.
public class Weapon : MonoBehaviour
{
...
// How much time has to pass between two bullets fired?
[SerializeField] private float bulletDelay = 0.1f;
// A flag indicating whether this weapon can currently shoot
private bool canShoot = true;
public void Shoot()
{
// while the flag is false do noting, you can't shoot now
if(!canShoot) return;
// ... Your shoot stuff here
// set the flag because you just shot
canShoot = false;
// Invoke the CooldownFinished method after bulletDelay has passed
Invoke(nameof(CooldownFinished),bulletDelay);
}
private void CooldownFinished()
{
canShoot = true;
}
}
If you rather want to provide a "rate" then simply invert
[SerializeField] private float bulletsPerSecond = 10f;
and then
Invoke(nameof(CooldownFinished), 1f / bulletsPerSecond);

Updating Enemy Health bar in unity 2d

Why is this connecting all the health bars of my enemies together, even though their actual health is decreasing at its specified rate?
public class FillHealth : MonoBehaviour
{
Image HealthBar;
private NormalMonster normalMonster;
// Start is called before the first frame update
void Start()
{
HealthBar = GetComponent<Image>();
normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
}
// Update is called once per frame
void Update()
{
UpdateHealthLeft();
}
public void UpdateHealthLeft()
{
if (normalMonster.healthLeft > 0)
{
HealthBar.fillAmount = normalMonster.healthLeft / normalMonster.setHealth;
}
}
}
This is the script that is being referenced in FillHealth. As far as I understand it, since the variable isn't static, then the values should not be shared. It should find fill the health bar for each individual enemy.
public class NormalMonster : MonoBehaviour
{
private float _normalSpeed = 2f;
private float _BaseHealth = 20f;
private float _HealthModifier;
public float setHealth;
public float healthLeft;
// Start is called before the first frame update
void Start()
{
UpdateHealth();
}
// Update is called once per frame
void Update()
{
NormMonMov();
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Arrows")
{
healthLeft -= Arrows.Damage;
Destroy(other.gameObject);
if (healthLeft <= 0f)
{
Destroy(this.gameObject);
EarnedGold.earnedGold += 7;
Spawn_Manager.enemyCount--;
}
}
}
public void UpdateHealth()
{
if (StageMode.StageLvl > 5)
{
_HealthModifier = (StageMode.StageLvl * 0.01f) * _BaseHealth;
setHealth = Mathf.Round(_BaseHealth + _HealthModifier);
}
else
{
setHealth = _BaseHealth;
}
healthLeft = setHealth;
}
public void NormMonMov()
{
transform.Translate(Vector3.left * _normalSpeed * Time.deltaTime);
transform.position = new Vector3(Mathf.Clamp(transform.position.x, -7.0f, 10), transform.position.y, 0);
}
}
Any help would be greatly appreciated for this guy who just start playing with unity this weekend.
I believe the issue is with normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
If you have two Monsters
Both have two scripts attached, FillHealth and NormalMonster
Both the "FillHealth" scripts look for the FIRST gameobject in the scene that has a script with tag NormalMonster so both monsters are pointing to the exact same NormalMonster script (the first in the list)
Change "GameObject" capital G to "gameObject" lower case g
Still not the best way to code this, but that may work I think
Instead of getting the image, get the rect transform, like this
public RectTransform healthBar;
and change the length with:
healthBar.sizeDelta = new Vector2(normalMonster.healthLeft,healthBar.sizeDelta.y);

Unity How to remove gameobject if they are on same line?

I am making a game like 2 cars. And I have written code to create a instantiater. It is a car game and there are 4 lanes. Let me show you a picture of my game and yeah this is solely for practise.
Player should avoid square objects and eat circle objects but sometimes 2 square objects get spawned in a same lane making impossible for player to win. I have written this script to control that but I failed. Please help me. At least have a check to my DetectSameLaneFunction(). And tell me what I am doing wrong.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour {
public GameObject[] objects;
public float delaytime = 2f; // this is separate for each prefab with which script is attaches
public float spawnrate = 1f; // this is separate for each prefab with which script is attaches
public static int lastgameobjectindex;
public static GameObject lastgameobject;
public static GameObject SecondLastGameObject;
private float loadingtime;
private GameObject go; // just a temporary variable
public static List<GameObject> spawnobjects = new List<GameObject>();
// Use this for initialization
void Start () {
loadingtime = delaytime;
}
// Update is called once per frame
void Update () {
if (Time.time > loadingtime)
{
float randomness = spawnrate * Time.deltaTime;
if ( randomness < Random.value)
{
Spawners();
}
NextLoadTime();
}
}
private void Spawners()
{
int spawnnumber = Random.Range(0, 2);
GameObject go = Instantiate(objects[spawnnumber]) as GameObject;
go.transform.position = this.transform.position;
spawnobjects.Add(go);
Debug.Log(spawnobjects[spawnobjects.Count-1]);
DetectSameLaneObjects();
/* if (spawnobjects.Count > 4)
{
spawnobjects.RemoveAt(0);
}*/
}
private void DetectSameLaneObjects()
{
if (spawnobjects.Count > 3)
{
lastgameobject = spawnobjects[spawnobjects.Count - 1];
SecondLastGameObject = spawnobjects[spawnobjects.Count - 2];
lastgameobjectindex = spawnobjects.Count - 1;
if (SecondLastGameObject.transform.position.x != lastgameobject.transform.position.x
)
{
if (Mathf.Abs(lastgameobject.transform.position.x- SecondLastGameObject.transform.position.x) < 2.3f)
{
Debug.Log("Destroy function getting called");
Destroy(spawnobjects[lastgameobjectindex]);
spawnobjects.RemoveAt(lastgameobjectindex);
}
}
}
}
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(this.transform.position, 0.6f);
}
void NextLoadTime()
{
loadingtime = Time.time + delaytime;
}
}

How do I get an object to move and swap places with another object, on a mouse clic

I have a script so far that moves an object a small distance upon a mouse click, however I want to change it so that when I click this object, it swaps places with another obejct next to it, instead of just the small distance it is moving now. I am a little confused on how to do this, because I am new to unity.
using UnityEngine;
using System.Collections;
public class NewBehaviourScript: MonoBehaviour
{
public float movementSpeed = 10;
void Update(){
if ( Input.GetMouseButtonDown(0))
{
transform.Translate(Vector3.right * movementSpeed * Time.deltaTime);
}
}
}
Try this:
using UnityEngine;
using System.Collections;
public class NewBehaviourScript: MonoBehaviour {
public GameObject objectA; //Needs to be initialized in the editor, or on Start
public GameObject objectB; //Needs to be initialized in the editor, or on Start
public float movementSpeed = 10;
private Vector3 posA = Vector3.zero; //Vector3.zero is for initialization
private Vector3 posB = Vector3.zero; //Vector3.zero is for initialization
void Update() {
if ( Input.GetMouseButtonDown(0)) {
posA = objectA.gameObject.transform.position;
posB = objectB.gameObject.transform.position;
objectA.gameObject.transform.position = posB;
objectB.gameObject.transform.position = posA;
}
}
}
This just saves each objects position into the posA and posB variables, then you move objectA to posB and objectB to posA.
-OR-
Now if objectB is always a different object (NOT constant) and you aren't sure how to find the nearest object, you could use a raycast. Add the following function to your code:
gamObject NearestObject () {
int dist;
int nearestIndex;
//Create an array to contain objects to be hit by the raycast
RaycastHit[] nearby;
//Hit all objects within 100 units with a raycast, change the 100 as needed
nearby = Physics.RaycastAll(objectA.transform.position, transform.forward, 100.0f);
//Check if there is at least one object
if(nearby.Length > 0) {
//If there is only one object and it's not objectA
if(!(nearby.Length == 1 && nearby[0].transform == objectA.transform)) {
dist = nearby[0].distance;
nearestIndex = 0;
for (int i = 1; i < nearby.Length; i++) {
if(nearby[i].transform != gameObject.transform && nearby[i].distance < dist)
dist = nearby[i].distance;
nearestIndex = i;
}
}
} else {
//There is only one object in the raycast and it is objectA
nearestIndex = -1;
}
} else {
//There are no objects nearby
nearestIndex = -1;
}
//nearestIndex will only be negative one if there are no objects near objectA, so return null
if (nearestIndex == -1) {
return null;
} else {
//return nearest object to update
return nearby[nearestIndex].gameObject;
}
}
Finally, change Update to:
void Update() {
if ( Input.GetMouseButtonDown(0)) {
objectB = NearestObject ();
if (objectB != null) {
posA = objectA.gameObject.transform.position;
posB = objectB.gameObject.transform.position;
objectA.gameObject.transform.position = posB;
objectB.gameObject.transform.position = posA;
}
}
}
using UnityEngine;
using System.Collections;
public class NewBehaviourScript : MonoBehaviour
{
//making them public just to be able watch values change in game mode
public float movementSpeed = 10;
public GameObject g1;
public GameObject g2;
public Vector3 vec1;
public Vector3 vec2 = new Vector3(2F, 2F, 2F);
public bool swapBack = false;
void Start()
{
g1 = GameObject.Find("Cube");
g2 = GameObject.Find("Sphere");
vec1 = new Vector3(g1.gameObject.transform.position.x, g1.gameObject.transform.position.y, g1.gameObject.transform.position.z);
vec2 = new Vector3(g2.gameObject.transform.position.x, g2.gameObject.transform.position.y, g2.gameObject.transform.position.z);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
transform.Translate(Vector3.right * movementSpeed * Time.deltaTime);
swap(swapBack);
}
}
public void swap(bool back)
{
if (back)
{
g1.transform.position = vec1;
g2.transform.position = vec2;
swapBack = false;
}
else
{
g1.transform.position = vec2;
g2.transform.position = vec1;
swapBack = true;
}
}
}

Categories