Ghost enemy AI that follows the player by copying the players position - c#

Okay, the plan is simple.
My plan is to make a simple AI that record every player.positions, and then follow the player by using those positions. So the AI will always be some steps behind the player. But when the player stops moving the AI should collide with player and then the player dies.
So my problem is when the player has been chased down by the AI, but it always runs out of position before the enemy AI is ever able to touch the player...
I used Queue for making a list of position, this was recommended by someone after I tried with List<>.
Here's a video showing the problem
public Transform player;
public Transform ghostAI;
public bool recording;
public bool playing;
Queue<Vector2> playerPositions;
public bool playerLeftRadius;
void Start()
{
playerPositions = new Queue<Vector2>();
}
// Update is called once per frame
void Update()
{
if (playerLeftRadius == true)
{
StartGhost();
}
Debug.Log(playerPositions.Count);
}
private void FixedUpdate()
{
if (playing == true)
{
PlayGhost();
}
else
{
Record();
}
}
void Record()
{
recording = true;
playerPositions.Enqueue(player.transform.position);
}
void PlayGhost()
{
ghostAI.transform.position = playerPositions.Dequeue();
}
public void StartGhost()
{
playing = true;
}
public void StopGhost()
{
playing = false;
}
private void OnTriggerExit2D(Collider2D other)
{
Debug.Log("Player leaved the zone");
playerLeftRadius = true;
}
How do improve it so it will be able to touch the player?

At the moment the player touch the zone, method OnTriggerExit2D() is called. Then, method PlayGhost() is called and Record() is stoped. So, the ghost can't record player.positions after player out zone.
You can remove else in method FixedUpdate() to fix it.
private void FixedUpdate()
{
if (playing == true)
{
PlayGhost();
}
Record();
}

Related

How can I bring a Player to ignore collision with another GameObject in code?

Im currently trying to code a health system for my game and I want my Player GameObject to ignore collision with the Health Potion GameObject if the Player has max health. My problem is that I cannot simply turn off the collision between the Player Layer and Health Potion Layer because I only want to ignore collision if the Player has Max Health. I tried doing it myself but it didn't work. Here's my code:
public class ExampleCodeUA : MonoBehaviour{
public int PlayerMaxHealth = 100, PlayerCurrentHealth;
public HealthBar healthBar;
private void Start()
{
PlayerCurrentHealth = PlayerMaxHealth;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("HealthPotion"))
{
if (PlayerCurrentHealth == PlayerMaxHealth)
{
Physics2D.IgnoreLayerCollision(6, 7);
}
else if (PlayerCurrentHealth > 50)
{
GetHealth(PlayerMaxHealth - PlayerCurrentHealth);
}
else if (PlayerCurrentHealth <= 50)
{
GetHealth(50);
}
}
}
void GetHealth(int healing)
{
PlayerCurrentHealth += healing;
healthBar.SetHealth(PlayerCurrentHealth);
}
}
You don't want to modify your physics configuration at runtime. What you could do to avoid the collision... is to make it impossible, by having nothing to collide with. Both of your objects have a collider. What you could do is to disable all existing HealthPotion colliders by modifying their code. An implementation could be the following:
using System.Collections.Generics;
using UnityEngine;
public class HealthPotion : MonoBehaviour
{
private static List<HealthPotion> _existingPotions = new List<HealthPotion>();
public static void EnablePotionsCollider(bool value)
{
foreach (var potion in _existingPotions)
{
potion._collider.enabled = value;
}
}
private Collider _collider;
void Awake()
{
_collider = GetComponent<Collider>();
}
void OnEnable()
{
_existingPotions.Add(this);
}
void OnDisable()
{
_existingPotions.Remove(this);
}
}
Then you just have to call the method when you want to enable/disable by doing
HealthPotion.EnablePotionsCollider(value you want);

Everything breaks after using PlayOneShot

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Unity2D: Making a cloned particle system fire once only

I a have this enemy prefab that gets instantiated from a spawn point in my game, on the enemy prefab I have a particle system componet that if the player shoots the enemy more than 2 times the particle system will play. However I'm having problems with firing the particle system on only one of the enemy prefab that the player shot. Is there a way when the enemy prefab gets shot at to fire the particle system on that enemy prefab rather than the particle system playing when the enemy prefab gets shoot at and any other enemies prefabs that didn't get shot at without using ontriggerenter2d as I had some problems.
This is the script attached to the player:
public GameObject enemy;
public static bool firePar = false;
public static int combo = 0;
void Start (){
firePar = false;
combo = 0;
}
void OnTriggerEnter2D(Collider2D col) {
if(col.tag == "Enemy") {
Destroy(col.gameObject,0.2f);
}
}
void Update() {
if(combo >= 10) {
firePar = true;
} else if (combo < 10) {
firePar = false;
}
}
This is the code on the enemy (for firing the particles):
private ParticleSystem par;
void Start (){
par = GetComponent<ParticleSystem>();
}
void Update () {
if(Playerbullet.firePar == true) {
par.Play();
} else {
par.Stop ();
}
}

Audio play issue on button click

I'm working with unity and have problem with audio. Here is scenario when user click on button, Object falls on ground and destroy. When click on button sound effect of object falling is play. And destroy, Object is instantiate again then same click sound effect is play again. But when one object is falling and does not collide at this time user click again that button sound play again. I want that when one object is complete destroy than again click happen and sound is play.
Code CubeScript:
public class Cube : MonoBehaviour {
Rigidbody2D body;
void Start () {
body = GetComponent<Rigidbody2D>();
body.isKinematic = true;
}
}
Code ColliderScript:
public class Ground : MonoBehaviour {
private Button bt;
public GameObject cube;
public AudioSource source;
public AudioClip clip;
void Start () {
bt = GameObject.FindGameObjectWithTag ("Button").GetComponent<Button> ();
bt.onClick.AddListener (() => Fall ());
}
void OnCollisionEnter2D(Collision2D col) {
Destroy (col.gameObject);
Instantiate (cube,new Vector3(0f,4.19f,0f),Quaternion.identity);
}
public void Fall(){
GameObject.FindGameObjectWithTag ("Player").GetComponent<Rigidbody2D> ().isKinematic = false;
source.PlayOneShot(clip);
}
}
void OnCollisionEnter2D(Collision2D col) {
Destroy (col.gameObject);
Instantiate (cube,new Vector3(0f,4.19f,0f),Quaternion.identity);
isFalling = false; // here
}
private bool isFalling = false; // here
public void Fall()
{
GameObject.FindGameObjectWithTag ("Player").GetComponent<Rigidbody2D> ().isKinematic = false;
if(isFalling == false){
source.PlayOneShot(clip);
isFalling = true; // here
}
}
Pretty much when you press, it calls Fall, if nothing is falling down, the sound happens. On Collision, the isFalling is reset. Tho I am not entirely sure about your logic.

Camera shake on collision in unity 3d

http://answers.unity3d.com/questions/212189/camera-shake.html
I've followed the question's answer above to try and get a camera shake working for my first person camera. But I've tried to modify it so that the camera shakes from an invisible collision box.
So far my camera shake script looks like this;
public bool Shaking;
private float ShakeDecay;
private float ShakeIntensity;
private Vector3 OriginalPos;
private Quaternion OriginalRot;
void Start()
{
Shaking = false;
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
DoShake();
Debug.Log("The camera trigger has hit");
}
}
void Update ()
{
if(ShakeIntensity > 0)
{
transform.position = OriginalPos + Random.insideUnitSphere * ShakeIntensity;
transform.rotation = new Quaternion(OriginalRot.x + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.y + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.z + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.w + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f);
ShakeIntensity -= ShakeDecay;
}
else if (Shaking)
{
Shaking = false;
}
}
void OnGUI() {
if (GUI.Button(new Rect(10, 200, 50, 30), "Shake"))
DoShake();
//Debug.Log("Shake");
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}
I know the code works 100% via the gui button. This script is attached to the camera on the first person controller. An invisible collision box with the tag ShakeTrigger is in the game. However, the debug log doesn't get called at all and I'm unsure why.
If anyone needs any more information just let me know.
Thanks in advance :)
If the script is attached to your camera, then OnTriggerEnter is looking at the camera for a trigger call, not the collision box.
One thing you could do is stick the OnTriggerEnter into a new script and put that inside the collision box. Then have that do a SendMessage along these lines:
GameObject.Find("Camera").SendMessage("DoShake");
EDIT: To answer Jerdak's questions.
The code bellow would be within the TriggerBox:
void Start()
{
...
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
GameObject.Find("Camera").SendMessage("DoShake");
Debug.Log("The camera trigger has hit");
}
}...
and this would be within the Camera:
void Start()
{
...
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}...
This way, triggerbox is responsible for detecting triggers and only ever sends a message to the camera when right kind of object goes through it. The camera is then responsible for doing the shaking.
To shake your camera on the collision or Trigger you need to first Make function of Your Shake that you can also call from other scripts
Something like
public class ShakeCamera : MonoBehavior
{
public bool canShake;
private void Update()
{
if(canShake)
DoShake()
}
public void DoShake()
{
// Shake Logic
}
public void StartShake()
{
canShake = true;
}
public void StopShake()
{
canShake = false;
}
}
And from your other script whenever you trigger the target object you can call it like this
public class TriggerScript: MonoBehavior
{
public ShakeCamera shakeCamera;
private void Start()
{
shakeCamera = FindObjectOfType<ShakeCamera>();
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StartShake();
}
}
void OnTriggerExit(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StopShake();
}
}
}
I am attaching one reference video for you, May be it would help you in better camera shake. Hope it was helpful.
You can refer to this video I made https://youtu.be/9X_JXexwfR4
If you have set up rigidbody then change the interpolate from none to interpolate.

Categories