Unity 2D same state for different enemies issue - unity3d

I'm making 2D platformer and im getting weird bug - when i hit one enemy, every one else of that type getting the same state. I mean i wanted them to enter "angry" state when my player hit one of them with sword.
Problem looks like this:
->Hit Enemy1 with sword,
->Enemy1 is getting damage and going to "angry" state,
->Enemy2/3/4/5 didn't get damage, but went in "angry" state.
If getting damage is individual for every one of them so why am i suffer that kind of situation. I think the cause is in my Damage Script. If anyone have an idea i would be glad to hear it. Thanks in advance.
public void DealDmg()
{
blob = FindObjectOfType<EnemyScript>();
if (attackPos.gameObject.activeSelf == true)
{
Collider2D[] enemiesToDamage = Physics2D.OverlapCircleAll(attackPos.position, attackRange, whatIsEnemy);
for (int i = 0; i < enemiesToDamage.Length; i++)
{
EnemyScript enemyScript = enemiesToDamage[i].GetComponent<EnemyScript>();
enemyScript.GetComponent<EnemyScript>().TakeDmg(damage);
if (gameObject.GetComponent<PlayerControls>().facingRight == true)
{
enemyScript.GetComponent<Rigidbody2D>().AddForce(new Vector2(30f, 20f), ForceMode2D.Impulse);
StartCoroutine("ResetValues");
blob.CalmLogic();
}
else if (gameObject.GetComponent<PlayerControls>().facingRight == false)
{
enemyScript.GetComponent<Rigidbody2D>().AddForce(new Vector2(-30f, 20f), ForceMode2D.Impulse);
StartCoroutine("ResetValues");
blob.CalmLogic();
}
}
attackPos.gameObject.SetActive(false);
}
}
public IEnumerator ResetValues()
{
gotHit = true;
blob.gotAngry = true;
yield return new WaitForSeconds(resetValuesDelay);
gotHit = false;
yield return new WaitForSeconds(resetValuesDelay);
blob.gotAngry = false;
}

Track enemy state inside the enemy
Right now it looks like your code is running on the player and the player is keeping track of enemy state. This is wrong. Your player script should never know anything about (much less modify!) the properties of your enemy. All the player should do is:
when the player successfully hits an enemy, the player tells the enemy "you've been hit!"
The enemy script then takes that information (such as how much damage, what damage type it was, etc) and deal with itself: reducing the damage ("am I wearing armor?"), enhancing the damage ("am I weak to fire?") and so on and so forth...like getting angry at the player and the resulting cooldown before its calm again.
Your player script does not need to know (and does not care!) about this information and any attempt to try and handle it here is folly. As soon as you change how many enemies there are, or create a new enemy type that does something ELSE you end up with a giant pile of useless junk you can't manage.

Related

Am I understanding something incorrectly about Physics.CheckBox?

Basically on my map I'm trying to use Physics.CheckBox detect when the player is in certain areas to determine where my enemies will spawn at. I am using a layer mask to detect when its colliding with the player and Gizmos to visualize this box in the editor. The issue I'm having is that it will return true even when the player isn't inside the box. I have verified every single other game item does not have the player layer mask causing it to return true when it hits something else. The kicker is Physics.CheckSphere works perfectly except for the fact that my map is square, not circle, so I can't use check sphere because I can't cover all of the areas I need to cover.
Code for both is as follows, note that both of these lines are not in my script at the same time I alternated them out for testing:
atNeighborhood = Physics.CheckSphere(spawnAreas[0].transform.position, neighborhoodRange, playerLayer);
atNeighborhood = Physics.CheckBox(spawnAreas[0].transform.position, neighborhoodRange, Quaternion.identity, playerLayer);
Why would the CheckBox return true when colliding with items not in the layer mask but the CheckSpere works perfectly and only returns true when colliding with the player? Anyone have any idea?
LET ME KNOW IF THERE ARE ANY PROBLEMS OR ERRORS IN COMMENTS. THANKS!
Ok. CheckBox can get kind of confusing sometimes. I would reccomend something else.
You could use Empty Game Objects with colliders on them and put them where ever you want. IsTrigger must be set to true. Imagine these as "zones", where whenever you step in one, something can happen.
All you have to do is set a certain tag to each zone to activate different things.
Note: Player does not need rigidbody, but it would be a whole lot less messy if you did.
Here is a script if your player does have a rigidbody (put this script on your player):
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("Zone 1"))
{
SpawnZombies();
}
}
Player doesn't have rigidbody:
If your player does not have a rigidbody, you could put a bunch a script on each one called "zone activator".
Important Notes for this version:
Your player must have a collider and a unique tag.
On each zone add a rigidbody.
Make sure detectCollisions is false!
Make sure useGravity is false!
This zone detector should have it's collider be a trigger;
(You do not want this thing to move!)
You can now create a script that goes on each zone:
public string message;
public bool inZone;
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = true;
}
}
void OnTriggerExit(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = false;
}
}
You must then reference this in the player's script
public ZoneDetector[] allZones;
void Update()
{
//.....
foreach (ZoneDetector zone in allZones)
{
if (zone.inZone == true)
{
if (zone.message == "zone 1")
{
DoZone1();
}
if (zone.message == "zone 2")
{
DoZone2();
}
}
}
}

How to find out who shot a projectile

So I am trying to make a multiplayer game with abilities sort of like Overwatch/Paladins. All in all, one ability should be a sort of projectile that moves across the ground and allows that player to teleport to its position at any time while it is alive. I can't find the solution to teleporting only the player that shot it since thus far in my tests, when one player activated their ability, all players would teleport. How can I solve this?
My code:
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
GetComponent<playerController>().heldAbility = "gateCrash";
if (GetComponent<playerController>().heldAbility == "gateCrash")
holding = true;
else
holding = false;
if (holding && Input.GetMouseButtonDown(0))
PhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", "GateCrashModel"), spawnPos, transform.rotation, 0);
}
This code is attached to the projectile:
public float speed = 10;
PhotonView pv;
private void Awake()
{
pv = transform.GetComponent<PhotonView>();
}
private void FixedUpdate()
{
transform.Translate(transform.forward * speed * Time.fixedDeltaTime);
}
I guess that I should make have something as instatntiation parameter but idk what.
A simple approach would be to add a shotOwner property to each of your projectiles. Every time a projectile is fired, update shotOwner to point to the player object that fired the shot. (This will also let you implement "Player_X killed Player_Y" functionality, among other things.)

Unity: Player jumps fine, but falls extremly slow

I am currently building a little 2D platformer with a character that can jump. This is how the jumping looks (FixedUpdate):
if (jump)
{
if (isGrounded)
{
isGrounded = false;
rb.AddForce(Vector2.up * (jumpHeight * counterForJumpHeight) * Time.deltaTime, ForceMode2D.Impulse);
jump = false;
anim.SetBool("bool_anim_isJumping", true);
}
if (timer != null)
timer.Stop();
counterForJumpHeight = jumpMulitMin;
jumpAlreadCharging = false;
}
Looks perfect for every jump up and then falling back down.
HOWEVER: when the player JUST falls (like off a cliff oder something) without a jump it looks like he has the mass of a leaf. Sailing to the ground extremley slowly. Not accelerating at all. Just falling as in slow motion. Of course I can up the gravity, but that also affects the falling AFTER my jump and makes him look like a stone. As if the falling is sped up or something. But that doesnt make sense. Him falling AFTER a jump and him just falling off of something SHOULD look the same, right? But it doesnt.
These are my values for the RB:
YOu can either up your player's rigidbody Mass, or increase gravity in edit/projectsettings/ physics
I'm not 100% sure but this may have to do with the collision detection. Try changing the setting to continuous dynamic instead of discrete.
It can be a problem with the animator, if the animation contains changes to Rigidbody, it does weird stuff if you have set the Make Default in animation.
it was all my fault, there was no way of guessing it from your point of view.
Everything the player could jump from was tagged with "can_jump".
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "can_jump") // detect collision with ground game object
{
isGrounded = true;
deacceleratePerFrame = 1.5f;
anim.SetBool("bool_anim_isJumping", false);
}
}
This includes all edges. Removing this means I cannot jump atm anymore, however this caused the issue. I sure find another way :-) thank you all

Auto attacking on collision

For a sidescrolling game I need to attack the enemies which collides with my player range, here is how I do :
void OnCollisionStay2D(Collision2D coll) {
Enemy target = coll.gameObject.GetComponent <Enemy> ();
if (target != null) {
moving = false;
anim.SetBool ("attack", true);
target.Damage (damageValue);
}
}
The problem is that i deal damageValue damages at each frame, so I wanted to add a cooldown, like this:
void OnCollisionStay2D(Collision2D coll) {
Enemy target = coll.gameObject.GetComponent <Enemy> ();
if (target != null && time.time > nextFire) {
moving = false;
anim.SetBool ("attack", true);
target.Damage (damageValue);
nextFire = Time.time + 1;//I set the cooldown to 1 second
}
}
The problem is that the player is attacking one time, and never again, I tried to Debug.Log(Time.time) at the end of OnCollisionStay method, and the debug spam is stopping just before the time where Time.time > nextFire;
I tried to Debug.Log at the start of the method, and it seems like OnCollisionStay is not called anymore after this debug freeze, which occurs around 1s - time.deltaTime after the first attack.
So I don't know how to do, basically here it is a 1D side-scrolling, you can just go from right to left, this should be really simple but I don't get it.
You may consider to create a fire rate for the weapon so it is changeable to one bullet at a time or serial fire. You will have control over it. Also prefer raycasting instead of collision. There is a 2D platformer tutorials around but I very liked the one from Brackeys.
YouTube Playlist

UNITY Lose one life when player hits the water

I want to change the number of lives every time the player hits the water. I wrote this code so far:
public var dieSound:AudioClip;
static var lives = 3;
function Start () {
}
function Update () {
if(lives == 0)
{
Application.LoadLevel("menu");
}
}
public function OnGUI()
{
GUI.backgroundColor = Color.blue;
GUI.Button (Rect (10, 10, 100, 30), "Lives: " + lives);
}
function OnControllerColliderHit (hit : ControllerColliderHit)
{
if (hit.collider.tag == "Water")
{
// play dying sound
audio.PlayOneShot(dieSound);
// show mission fail UI
GameObject.Find("MissionTXT").guiText.enabled = true;
// wait until it's ended
yield WaitForSeconds(dieSound.length + 0.01);
transform.position = GameObject.FindWithTag("Respawn").transform.position;
if (transform.position == GameObject.FindWithTag("Respawn").transform.position)
{
GameObject.Find("MissionTXT").guiText.enabled = false;
lives = lives - 1;
}
}
}
The problem is that when the player hits the water, lives change from 3 to -120. I think that happens because the player is on the water for like 6-7 seconds. So character may hits the water 120 times until he goes back to the original position (Respawn position).
Can anyone help me with that please?
The first thing that comes to mind is as follows:
On your water GameObject, add a Collider component. I would think that a BoxCollider would fit in this scenario. Don't forget to mark the Is Trigger checkbox.
On your player GameObject, add both a RigidBody and a CharacterController (since it looks like you are using the CharacterController component). Make sure to check the RigidBody's Is Kinematic checkbox. Also, give your GameObject a meaningful tag like "Player".
Back to the water GameObject, add a new script that should look something like this:
public class Water : MonoBehaviour {
void OnTriggerEnter(Collider collider) {
if(collider.CompareTag("Player")) {
collider.SendMessage("Kill");
}
}
}
Back to the player GameObject, add a new script that should look something like this:
public class Player : MonoBehaviour {
public void Kill() {
//Perform all necessary steps to kill the player such as...
//Reduce the amount of lives by 1
//Play the death sound
//etc. etc. etc.
}
}
That's the "jist" of things, or this should at least get you started. Unity has some really good documentation and practically anything you need is there, you just have to know where to look. I'm not going to go in-depth of each thing I have mentioned above because as I've said, "Unity has some really good documentation." With that in mind, I highly recommend looking into each of the things I have mentioned. Hope this helps! =)