How to destroy child object of other player in UNET? - c#

Right now, I have a multiplayer game where the player fires projectiles at other players that have a certain amount of bubbles. Once a player has lost all of their bubbles, they lose.
The issue I had previously was that on one screen, a first player could shoot out a second player's bubbles, but not all of the bubbles on the second player's screen would be popped, so I am trying to sync it over the network somehow.
The issue I'm seeing is that NetworkServer.Destroy requires finding the GameObject you intend to destroy by its NetworkIdentity, but only the root Player GameObject is allowed to have the NetworkIdentity component on it. What's the best way I can sync the destruction of a child object of a player?

The https://unity3d.com/fr/learn/tutorials/topics/multiplayer-networking/ course is very similar to the game you describe. It has a Bullet script attached to the fired bullets. If the bullets touched something, it check if it's a player, and apply damage to it (the player prefab has a Health script attached to it).
using UnityEngine;
using System.Collections;
public class Bullet : MonoBehaviour {
void OnCollisionEnter(Collision collision)
{
var hit = collision.gameObject;
/* Code below is from the original example
var health = hit.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(10);
}*/
var bubble = hit.GetComponent<BubbleBehavior>();
if (bubble != null)
{
bubble.Popped(); // Implement a Popped function doing the magic
}
Destroy(gameObject);
}
}
In your case you can do the same, but instead of checking if the touched object is a player, simply check if it is a bubble, and do whatever you want from the touched bubble.
In the original example, they sync the health (it is a "SyncVar"), in your case you can sync the number of bubbles remaining.

Related

How to end a game in C# from a Collision

This is the current script that I have. I have a ball that is player, and I want a collision with my game object tagged as "pit" to end the game. When the game ends I want my game over canvas to pop up. The current script detects the hit when the player rolls over the pit, however, currently, nothing else happens. Both my player and pit have rigidbodies and colliders attached to them. I would really appreciate any help with how to get my code to work properly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Trigger : MonoBehaviour
{
public GameObject GameManager;
private void OnTriggerEnter(Collider other)
{
Debug.Log("hit detected")
if(other.gameObject.tag == "pit")
{
GameManager.GetComponent<Game_Manager>().EndGame();
}
}
}
My EndGame code is in my GameManager script. The is the part of that script that deals with ending the game.
public void EndGame ()
{
player.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezePosition;
hasGameStarted = true;
inMenuUI.gameObject.SetActive(false);
inGameUI.gameObject.SetActive(false);
gameOverUI.gameObject.SetActive(true);
}
If I'm correct in interpreting your question, your script successfully detects player death already. If you want a game over screen you have a couple of options:
You can create a UI canvas with a panel on it that obscures the whole screen and put the words Game Over.
Or, probably the better option is to create an entirely new Unity scene and make the Game Over canvas there. When you want to trigger the game over screen you simply use:
Scene manager.LoadScene("Scene name");
You have to add "using UnityEngine.SceneManagement;" at the top of your script. Also note you have to add your Game Over scene to the list of scenes in you game. I believe this can be done by navigating to File>Build settings>scenes and then pressing add open scenes assuming the game over scene is open.

How can I make a particle remove an object?

In my game a player plants a bomb, the bomb explodes creating a fire effect, I would like my fire to be able to kill the player(blue cylinder) and any boxes it collides with. My box and player have colliders. My fire effect is instantiated when the bomb explodes.
How can I make my fire effect destroy my player and box objects?
Can I say something like if fire collider hits player collider, destroy player?
My code for the bomb is as follows
Instantiate(Firebolt, bomb.gameObject.transform.position, Quaternion.identity);
Game Layout
You can add a collider to Firebolt as well and use OnCollisionEnter on it, checking if the object you hit is a player or a box.
This is a simple example that destroys any player or box it collides with:
void OnCollisionEnter(Collision collision) {
GameObject other = collision.gameObject;
// Here I'm using tag to detect if the hit object is a player or a box
// but you can use name or other methods
if (other.tag == 'Player' || other.tag == 'Box') {
Destroy(other);
}
}
check out https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnParticleCollision.html
you can apply it to the ParticleSystem or to the target GOs that should react to it.
(u also need to activate collision detection on Particle System AND set it to world.

Unity: How can I programmatically solve an animated sprite with multiple colliders causing scoring issues?

I have an animated sprite which has 8 frames of animation. Each frame has a PolygonCollider2D attached as an element of an array thus providing it with accurate collision detection no matter which frame is playing.
When I had only one collider on the sprite and it passed through the "score" object the player's score increased by 1 point.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Point")
{
score++;
scoreText.text = score.ToString();
return;
}
}
Polygon Collider 2D array in Unity inspector
Each collider in the array becomes active in turn...
public void SetColliderForSprite(int spriteNum)
{
colliders[batColliderIndex].enabled = false;
batColliderIndex = spriteNum;
colliders[batColliderIndex].enabled = true;
}
Now that each frame of the animation has its own collider I find that the score is increasing at an alarming rate as each frame plays multiple times whilst inside the "score" game object, triggering the point scoring logic multiple times, before the player leaves said object.
I'm wondering what the ideal programmatic solution is in order to ensure that on entering the "point" collider object the player only gets a score increment once before exiting the area?
Thanks in advance for any suggestions (and my apologies if this has been answered elsewhere).
EDIT: I was thinking about my post below and I think there is a simpler solution. You should be able to animate your collider shape rather than having 1 collider per animation frame. For example, see this question on How to update 2d colliders with sprite animation.
(And as a side note, you can also have multiple polygon colliders on each part of the sprite, and animate the colliders to move accordingly. For example, see this post on Animating Polygon Collider 2D. This alternative approach might make the suggestion I make in my post below simpler.)
One suggestion I have is to only increment the score when the first "Point" trigger is detected and then not incrementing the score again until the object has fully left. This can be implemented by keeping track of how many colliders (or which specific colliders) have entered and exited the object. Something like the following:
private const string PointTag = "Point";
private int _triggerCount;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == PointTag)
{
bool firstCollision = (_triggerCount == 0);
if (firstCollision)
{
score++;
scoreText.text = score.ToString();
}
_triggerCount++;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.tag == PointTag)
{
_triggerCount--;
}
}
However, once a collision is in progress, you will likely have to change the behaviour so that all colliders stay active until the object has fully left the collision area. This is because if you disable the collider, the OnTriggerExit2D event will not fire.
This solution may not be acceptable depending on how accurately you need to detect that the VampireBat has left the collision area.
If it doesn't need to be very accurate, you can simplify the solution by adding a 9th collider with a unique tag that covers the entire area of the sprite's animation. By using this 9th collider to determine when the player has left the collision area, you would not need to worry about affecting the code related to showing the appropriate collider per animation frame. (But note that the code example here would have to be modified so that the OnTriggerExit2D uses this 9th collider object.)
If it needs to be more accurate, I think there is likely a solution similar to what I suggested based on which colliders are currently active, the details of your collider shapes & animation frames, and OnTrigerEnter2D, OnTriggerStay, and OnTriggerExit2D. But note that you may also need to be careful that a collision is not re-triggered immediately. (For example, the object may have fully left the collision area, but the next animation frame might trigger a collision again immediately.)
Thanks for the response. Your edit got me to thinking about child objects. I added a child collider (with an index value of 9) then checked for a collision with that.
child object in inspector
Referenced the child collider in the collider array.
Collider array in inspector
After that it was just a matter of referencing it in the player controller script.
// Update is called once per frame
void Update () {
//Player input
if (Input.GetMouseButtonDown(0))
{
bat.velocity = new Vector2(0, 4);
//ADD BAT FLAP SOUND LATER!!!
}
scoreText.text = score.ToString();
Debug.Log("SCORE: " + score);
}
//Point increment function
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Point" && batColliderIndex == 9)
{
score++;
return;
}
}
I now have pixel perfect collision detection against obstacles and a score which increments correctly.
Thanks again.

Checking Animator on Unity script only work on first Object

Let me explain the question first :)
I have a hero and an enemy. It is a fighting game. Both hero and enemy have idle, block, punch and getHit animations and states in the Animation Controller. The hero and enemy have scripts attached to them. The hero is controlled by player and then enemy is controller by AI.
Now I attach the script to hero first and then the enemy. Now when the enemy punches and if hero is not defending the hero takes a hit. But if the enemy is not blocking and the hero hits the enemy doesnt take the hit. This is because the script was attached to hero first.
Now if I remove the script from both and attach the enemy script first to enemy and then attach the hero script to hero. The wiseversa is true. In the enemy the hits will be registered and on the hero the hits wont be registered.
I am not sure why this is happening and I have tried different things and still the problem persists.
I also tried looking everywhere online for solution but none of them addressed my concern.
Thanks :)
below is the enemy script that checks is enemy can take hit
void Update () {
myTick++;
currentTick = myTick;
GameObject player = GameObject.Find("theDude");
Animator pAnim = player.GetComponent<Animator>();
//Getting Hit
if (pAnim.GetBool("tIsPunching"))
{
if (anim.GetBool("bEnemyIsDefending") == false)
{
Debug.Log("enemy got hit");
anim.SetTrigger("tEnemyGotHit");
anim.SetBool("bEnemyIsDefending", true);
}
}
}
And here is the hero script that checks if hero can take hit.
void Update () {
totalTime += Time.deltaTime;
GameObject enemy = GameObject.Find("Enemy");
Animator eAnim = enemy.GetComponent<Animator>();
//Getting Hit
if (eAnim.GetBool("tEnemyIsPunching"))
{
if (anim.GetBool("bIsDefending") == false)
{
Debug.Log("player got hit");
anim.SetTrigger("tGotHit");
}
}
}
Instead of get object I used to have a public GameObject and attached the hero and enemy in the respective classes. But It doesnt make any difference.
I think your problem lies in your use of the triggers tEnemyIsPunching and tIsPunching. Triggers get reset whenever they cause a transition to occur
(see: https://docs.unity3d.com/Manual/AnimationParameters.html).
In your case tIsEnemyPunching or (tIsPunching) is getting reset in the same frame as it gets set. Here is an example of what one update loop may look like in your game if the hero script is added first:
Hero Update()
Check if enemy is punching
He is not, so don't do anything
Enemy Update()
Punch! Set 'bIsEnemyPunching' = true
Animation Update()
bIsEnemyPunching is true so transition to the punching animation
reset bIsEnemyPunching = false
On the next update let's look at what happens in the hero update:
Hero Update()
Check bIsEnemyPunching
bIsEnemyPunching was reset in the previous frame, so it is false
Since the enemy isn't punching don't do anything
So Hero never sees the punch because bIsEnemyPunching got reset before Hero had a change to check it.
This is why the order of adding the scripts matters. Whichever script updates first is able to punch because the second script will see the trigger before it gets reset. However, the second script to update will never be able to punch because the trigger gets reset before the other scripts gets a chance to update.
One solution is to check the name of the animation state instead of the trigger value.
static int punchStateHash = Animator.StringToHash("Punch");
AnimatorStateInfo enemyState = eAnim.GetCurrentAnimatorStateInfo(0);
if (enemyState.nameHash == punchStateHash){
//The enemy is punching!
}
Alternatively, whenever a punch is triggered just call a function on whichever character is getting punched. So when the enemy punches the hero the Punch(...) function on the enemy should call TakePunch(...) on hero (or whatever you want to call those functions). The hero then checks his own state to determine if he is blocking.
Additional Note
You should avoid using the following code in an update function:
GameObject player = GameObject.Find("theDude");
Animator pAnim = player.GetComponent<Animator>();
These functions are very slow because Unity must search all objects for the one called theDude and then search all it's components to find the animator. Do this once and save off the result.

Call upon multiple animation events on player

If I have a player and let's say a red ball hits my player and the player goes on fire... OK easy enough I did that. But now I want to add to the code below under the if statement that a blue ball hits my player and freezes or a black ball and causes death animation... But it's just not working out to add multiple animation events to 1 player.
Public GameObject GoneGo;
void OnTriggerEnter2D(Collider2D collisionObject)
{
if (collisionObject.gameObject.tag == "Orb")
{
PlayGone();
}
}
void PlayGone()
{
GameObject gone = (GameObject)Instantiate(GoneGo);
gone.transform.position = transform.position;
}
What you could do is perform a name check on the collider that impacts the player. You already have it checked the tag, have it check for redball,blueball, or blackball as the GameObject name afterwards.
You can handle multiple animations by looking into unitys mechanim system, which is the animator component (not animation component).
https://community.mixamo.com/hc/en-us/articles/203879268-Unity-Mecanim-Animation-Basics
What you would do after setting up the animator is have it SetTrigger for different trigger parameters. Or, you could use something like SetBool with a bool in the parameters like HitByBlueBall or HitByRedBall.
Hope this helps!

Categories

Resources