unity 2D: simple enemy shooting - c#

I'm working on a 2D game. If I wanted a simple enemy canon shooting side to side every 5 seconds, how would I go about it ?
I know that I would need to add a colider and rigidbody but not quite sure how to approach this, since am still grasping the whole idea
Red = Enemy/ Rough idea/Sketch
Thank you

What you want is to create a type of gameobject to be used as a 'bullet'. This gameobject when spawned has a script on it to make it travel in a certain direction.
You can move them using force (physics) or you can translate them from one place to another which is to move them 'absolutely' and ignore physics in the environment.
Then you can use a collider on this object to detect when it hits the player using either the OnCollisionEnter method or the OnTriggerEnter method.
There are some tutorials on it here so I hope they help.
Creating Shooting

First you need to think how your enemy should behavior, Just walk side by side or find the enemy as Navmesh.
I found this scritpt on unity website:
using UnityEngine;
using System.Collections;
public class EnemyAttack : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f; // The time in seconds between each attack.
public int attackDamage = 10; // The amount of health taken away per attack.
Animator anim; // Reference to the animator component.
GameObject player; // Reference to the player GameObject.
PlayerHealth playerHealth; // Reference to the player's health.
EnemyHealth enemyHealth; // Reference to this enemy's health.
bool playerInRange; // Whether player is within the trigger collider and can be attacked.
float timer; // Timer for counting up to the next attack.
void Awake ()
{
player = GameObject.FindGameObjectWithTag ("Player");
playerHealth = player.GetComponent <PlayerHealth> ();
enemyHealth = GetComponent<EnemyHealth>();
anim = GetComponent <Animator> ();
}
//OntriggerEnter and On triggerExit the enemy will follow enemy everytime the player enter on collide and stop if player exit
void OnTriggerEnter (Collider other)
{
// If the entering collider is the player...
if(other.gameObject == player)
{
// ... the player is in range.
playerInRange = true;
}
}
void OnTriggerExit (Collider other)
{
// If the exiting collider is the player...
if(other.gameObject == player)
{
// ... the player is no longer in range.
playerInRange = false;
}
}
void Update ()
{
// Add the time since Update was last called to the timer.
timer += Time.deltaTime;
// If the timer exceeds the time between attacks, the player is in range and this enemy is alive...
if(timer >= timeBetweenAttacks && playerInRange && enemyHealth.currentHealth > 0)
{
// ... attack.
Attack ();
}
// If the player has zero or less health...
if(playerHealth.currentHealth <= 0)
{
// ... tell the animator the player is dead.
anim.SetTrigger ("PlayerDead");
}
}
void Attack ()
{
// Reset the timer.
timer = 0f;
// If the player has health to lose...
if(playerHealth.currentHealth > 0)
{
// ... damage the player.
playerHealth.TakeDamage (attackDamage);
}
}
}

Related

Move Object to the last position or position just outside the collision area

I am using a Player rigid body object and there are walls around the Player. These walls are restricting the Player to go through. The Player gets collided with these walls and then falls. The Player uses teleport function to jump from one area to next. Is there a way to make the Player jump to a position just outside the collision area after the Player is collided with these walls?
That is, Player A gets collided with the wall and does not jump to last position, but the position before the collision happened?
public GameObject Player;
public Vector3 PlayerPos;
public bool RecordPos = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(RecordPos == true)
{
PlayerPos = Player.transform.position;
}
}
public void OnTriggerEnter(Collider col)
{
if(col.gameObject.name == "Cube(3)" )
{
RecordPos = false;
Player.transform.position = PlayerPos;
}
}
In this script, the Player moves to last position it teleported from.
The "last" position before colliding is 1 frame before the collision. If you capture this position, the character will probably just fall on the obstacle again. Imagine you have a platform ___...___ and an obstacle. The easiest solution is to have 1 trigger at the left side of the obstacles and 1 trigger at the right side. If the player hasn't overcome the obstacle yet, he will be teleported to a chosen destination by you (before the obstacle) and if he's already overcome the obstacle, he will be teleported at the right side. __S_..._S__ (S stands for save/checkpoint trigger)
You need the following script on the gameobject with the Trigger collider. You also need to create a child object to the gameobject with the trigger:
private void OnTriggerEnter2D(Collider2D collision)
{
SaveManager.Instance.LastCheckpointOnHit = transform.GetChild(0).position;
}
And I suppose you have some sort of a singleton for data persistance. Now you can move the child gameobject whereever you want to teleport the player. And BTW I named the property LastCheckpointOnHit, because I was thinking of Holow Knight where if you get hit by spikes it instantly teleports you.
Then you just move the player: Player.transform.position = SaveManager.Instance.LastCheckpointOnHit;
In general when dealing with Rigidbody you shouldn't apply positions through the Transform component but rather through the Rigidbody.
I could imagine something like if you collide with a wall you get pushed away from the wall a bit in the direction where you came from like e.g.
[SerializeField] private float someDistanceThreshold = 0.01f;
[SerializeField] private Rigidbody _rigidbody;
private void Start()
{
if(!_rigidbody) _rigidbody = Player.GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
PlayerPos = _rigidbody.position;
}
public void OnTriggerEnter(Collider col)
{
// instead of the name I would rather use a tag later to cover all obstacles
if(col.gameObject.name == "Cube(3)")
{
_rigidbody.position -= (_rigidbody.position - PlayerPos).normalized * someDistanceThreshold;
// For stopping the player here
_rigidbody.velocity = Vector3.zero;
}
}

How to Move and Jump in Unity

I am making a First Person Shooter Game and am currently working on the jumping. I am using animation to jump as I have struggled with gravity before. However my problem is that my player cannot move if my jump script is attached to the player as well as the movement. However I know that it has nothing to do with the scripts as both scripts work but not simultaneously.
I believe it has something to do with the animation. There are 3 parts to the animation, Base, Running and Jump. If the player is moving the running animation is active, if the player has pressed the space bar then the jump animation is active. Jump animation is prioritised.
Is there any reason why the player cannot move and jump?
Here is the codes and animation controller:
Animation and Jump
public class Jump : MonoBehaviour
{
public bool run;
public float speed;
public GameObject player;
public Animator dgbanim;
public bool jump;
public float rejump;
public float rejumper;
// Start is called before the first frame update
void Start()
{
speed = 50;
run = false;
jump = false;
rejumper = 0;
rejump = 0;
}
// Update is called once per frame
void Update()
{
if (jump == false && Input.GetKey("space")) { dgbanim.SetBool("Jump", true); jump = true; rejump = 21; }
if (rejump > 0) { rejump = rejump - 1; }
if(rejump == 0) { jump = false; dgbanim.SetBool("Jump", false); }
if (Input.GetKey("w") || Input.GetKey("s") || Input.GetKey("a") || Input.GetKey("d") || Input.GetKey("up") || Input.GetKey("down") || Input.GetKey("left") || Input.GetKey("right")) { run = true; }
if (run == true) { dgbanim.SetBool("Running", true); run = false; }
else if (run == false) { dgbanim.SetBool("Running", false); }
}
private void OnCollisionEnter(Collision collision)
{
//if (collision.gameObject.tag != "EnemyBullets") { jump = true; rejump = 0; }
if (collision.gameObject.tag != "EnemyBullets") { jump = false; }
}
}
Movement
public class Movement : MonoBehaviour
{
public GameObject player;
public float speed;
// Start is called before the first frame update
void Start()
{
speed = 50;
}
// Update is called once per frame
void Update()
{
player.transform.Translate(Input.GetAxis("Horizontal") * speed * Time.deltaTime, 0, Input.GetAxis("Vertical") * speed * Time.deltaTime);
}
}
It seems like your animator have ‘apply root motion’ checked and its taking movement from animations rather than code, try unchecking it.
Animation can control some properties of a GameObject, such as the position. If you try to edit a GameObject's position in a script but an animation controls the position, the animation will override the script's changes to the position.
To see if this is the problem, you can open the animation window and select your player. If the player's position (in the inspector) is red, then the animation is controlling it.
I know of 2 ways to fix this:
Method 1 (not recommended): Edit the animation to move the root bone instead of the player
Most character models have a GameObject called "Armature" or "root" as a child. The position of your character should not be in the animation. If you need to move the character up in your animation, you can animate the "Armature" or "root" GameObject. In the screenshot below, note that in the animation window you don't see "PlayerPref: position" but instead see "Armature: position". This allows my movement script to move PlayerPref without the animation overriding the position.
Method 2 (recommended): Use the physics engine to jump
In your animation, don't make the player jump. Instead make the player "jump in place" i.e. maybe swing arms upward as if jumping but don't actually move upward. Just like Method 1, your animation should not set the player's position. The easiest way to do this is to click on "player: position" in the animation window and press delete.
Add a Rigidbody to your character. In your script:
private Rigidbody rig;
...
void Start(){
rig = GetComponent<Rigidbody>();
...
}
In your script when you trigger jumping, you can add rig.AddForce(jumpPower * transform.up);. This will make the physics system handle the jumping. If the player is jumping but there is something above, the physics engine will stop the player. With animation, the player will jump through any obstacles.

Unity3D: Having player light torches using ParticleSystem and OnCollisionEnter?

I'm trying to get my player to light torches in a scene, but not quite sure how to do this.
I have a torch prefab that has a particle system. Each time the player's torch collides into an unlit torch, I would like that torch to start burning.
I have been trying to follow the docs but have not been able to understand (https://docs.unity3d.com/ScriptReference/ParticleSystem.html, https://docs.unity3d.com/ScriptReference/ParticleSystem.Play.html).
Also have this question posted here: https://answers.unity.com/questions/1491419/having-player-light-torches-using-particle-system.html
My current code is below. I have each torch object tagged as torch, and my player tagged as Player. All particle systems, except the player's torch, have 'Play on Awake' off and prewarm on.
Any advice or tips?
Thanks!
/*
* Attach this script to all the torches. It will be used to start the fire
using OnCollision?/OnTrigger? See which is better
* Start with the particle effect/light being off, get all the components
* Turn the torches on when the player's torch collides with them
* 1.) Must make sure each torch object has a collider
* */
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StartFire : MonoBehaviour
{
public GameObject torch;
public ParticleSystem fireParticleSystem;
bool lightOn;
void Start()
{
lightOn = false; //Start with the light off
fireParticleSystem = GetComponent<ParticleSystem>(); //get Particle System
torch = GetComponent<GameObject>(); //get Torch
}
/*
* if player's torch hits this torch (that is not lit)
* Turn on the fire
* Set the light being on to true
* */
private void OnCollisionEnter(Collision collision)
{
if(this.gameObject.tag==("torch") && collision.gameObject.tag==("Player") && lightOn==false)
{
fireParticleSystem.Play(); //start the particle system
lightOn = true;
}
}
}
1.Create your ParticleSystem and change the tag of its GameObject to "torch".
2.Attach BoxCollider to the SphereCollider to that GameObject with the ParticleSystem.
3.Mark the IsTrigger of the collider created from #2 to be true because it doen't make sense to collide with a touch. It seems like you just want to detect when the player is touching it.
4.The touch script should attached to the player instead of the touch. Use OnTriggerEnter to handle the detection and detect when player touches the touch-light then use GetComponent to get the ParticleSystem and play it. Stop the particle in OnTriggerExit.
If you actually want player to collide and be stopped by the touch then ignore #2 and also use OnCollisionEnter and OnCollisionExit instead of OnTriggerEnter and OnTriggerExit.
Attach to the Player:
public class ParticlePlayer : MonoBehaviour
{
void OnTriggerEnter(Collider other)
{
//Make sure player is touching a touch
if (other.CompareTag("torch"))
{
//Get ParticleSystem from the Gameobject the player collided with
ParticleSystem ps = other.GetComponent<ParticleSystem>();
//Play Particle
ps.Play();
}
}
void OnTriggerExit(Collider other)
{
//Make sure player is touching a touch
if (other.CompareTag("torch"))
{
//Get ParticleSystem from the Gameobject the player collided with
ParticleSystem ps = other.GetComponent<ParticleSystem>();
//Stop Particle
ps.Stop();
}
}
}
I've used the following code in some of my projects:
private ParticleSystem _particleSystem;
private ParticleSystem.EmissionModule _emissionModule;
private void Awake()
{
_particleSystem = GetComponent<ParticleSystem>();
_emissionModule = _particleSystem.emission;
_emissionModule.enabled = false;
}
private void OnCollisionEnter(Collision collision)
{
_emissionModule.enabled = true;
_particleSystem.Play();
}
I believe you're missing the emission module.

Unity C# float value not decreasing on "OnCollisionEnter2d"

First have a look at my code
using UnityEngine;
using System.Collections;
public class Layer : MonoBehaviour {
public float health=150f;
void OnCollisionEnter2D(Collision2D beam){
if (beam.gameObject.tag == "Box") {
Destroy (gameObject);
}
Projectile enemyship = beam.gameObject.GetComponent<Projectile> (); // Retrieving enemyship
if (enemyship) {
Destroy (gameObject);
health = health - 100f; // its value is decreasing only once
Debug.Log (health);
if (health < 0f || health == 0f) {
Destroy (enemyship.gameObject); // this line not executing
}
}
}
}
In my above code the value of health is decreasing only once but OnCollisionEnter2D is working properly. That means on first collision the health value decreases by 100f and it becomes 50f but when it collides second time it's value is still 50f. And I have been looking since 3 hour for this solution. Please help me
I am adding little more thing. I am firing a projectile(laser) when I pressed space. So when laser hits twice object was supposed to be destroyed
Okay so first thing you're doing wrong is that your collision logic makes no sense at all. Your colliding object is "let's say a mortal object" which has to "die" whenever it's health is lower or equal to 0 but you're destroying it every time it collides with anything that has tag Box or has component of type Projectile.
To fix this first reduce the health based on these condition and then check if you want to destroy that object or not.
example code:
public float health = 150f;
void OnCollisionEnter2D(Collision2D beam)
{
float damage = 0f; // current damage...
if (beam.gameObject.tag == "Box")
{
// Destroy (gameObject); // do not destroy, just remove health
damage = health; // reduce health to 0
}
else
{
Projectile enemyship = beam.gameObject.GetComponent<Projectile> ();
if (enemyship)
{
// Destroy (gameObject); // again do not destroy.. just reduce health
damage = 100f;
}
}
health -= damage;
Debug.Log (health); // print your health
// check if dead
if (health < 0f || health == 0f) {
Destroy (gameObject); // this line not executing
}
}

Unity: Random NavMesh Agent speed C#?

I have Enemy prefab that has navmesh agent. The enemy is controlled by the down give script , how can i have random navmesh agent speed using the give script
Enemy Movement
public class EnemyMovement : MonoBehaviour
{
Transform player; // Reference to the player's position.
PlayerHealth playerHealth; // Reference to the player's health.
EnemyHealth enemyHealth; // Reference to this enemy's health.
UnityEngine.AI.NavMeshAgent nav;
void Awake ()
{
player = GameObject.FindGameObjectWithTag ("Player").transform;
playerHealth = player.GetComponent<PlayerHealth>();
enemyHealth = GetComponent <EnemyHealth> ();
nav = GetComponent <UnityEngine.AI.NavMeshAgent> ();
}
void Update ()
{
// If the enemy and the player have health left...
if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
{
// ... set the destination of the nav mesh agent to the player.
nav.SetDestination (player.position);
}
// Otherwise...
else
{
// ... disable the nav mesh agent.
nav.enabled = false;
}
}
}
Get random number with UnityEngine.Random.Range(yourMinf, yourMax5f);.
Assign that number to NavMeshAgent.speed.
For example, the snippet below will assign random speed between 2 and 5.
nav.speed = UnityEngine.Random.Range(2f, 5f);
Just put that inside your if statement. You may also be interested in other variables such as NavMeshAgent.angularSpeed and NavMeshAgent.acceleration

Categories