I have a problem with my script at Unity. I apply this script to my character and nothing happens when it reaches the collider, no HP is taken.
public int HP = 100;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void LoseHP(Collider col)
{
if(col.tag == "trap")
{
HP -= 100;
}
}
I'm not an expert of Unity but I'd use the method OnCollisionEnter that allows you to know if your object has begun to touch another rigidbody/collider. Here the link
Or another solution could be that you check in the Update() method if your object has been touched from another rigidbody. If the statement is true, call your LoaseHP(...) method.
also i'd consider using FixedUpdate() method if you want to increment a players health on being hit. Update() method may be called to often if you're not going to use the OnCollisionEnter method
Related
I am making a multiplayer game using Unet. Currently, the players can move independently (i.e., if user one presses d, then character one will move right if user two presses d then character two will move right) - however, both players shoot 'through' the host.
My movement code is inside the PlayerUnit script. It has one !isLocalPlayer check at the top, in the Update() method. This check is performed before the script detects for user inputs (i.e., w, s, mouse1, etc.).
This all works fine when the correct key is pressed; the proper action is performed.
Eg:
if (Input.GetKeyDown(up))
Jump();
Shooting works in a slightly different way as it involves a different script.
When mouse1 is pressed, the Shoot() method in PlayerUnit is called. This, in turn, uses a Command inside the PlayerUnit script to call a ClientRpc in a separate WeaponMaster script, which calls the Fire() coroutine.
In PlayerUnit:
private void Shoot ()
{
//WeaponMaster.wM.Shoot();
CmdShoot();
}
[Command]
void CmdShoot ()
{
WeaponMaster.wM.RpcShoot();
}
In WeaponMaster:
[ClientRpc]
public void RpcShoot ()
{
if (ammo != 0)
{
if (isAutomatic == true)
{
keepFiring = true;
StartCoroutine(Fire());
}
if (isAutomatic == false && noQuickFire == true)
StartCoroutine(Fire());
}
}
WeaponMaster has one !isLocalPlayer check as well, once again in the Update() method.
When debugging, I know that playerTwo's (client) Shoot() method is being called, but playerOne's RpcShoot is being executed. Everything works fine when playerOne shoots - hence the problem of both players shooting 'through' the host.
All the scripts are on the same object with one NetworkIdentity component.
I'm convinced the problem is just a missing authority check - but I've tried adding !isLocalPlayer tests in the various methods to no avail.
As said my guess is that a Singleton pattern in WeaponMaster.wM is not a good approach here since you get the wrong reference and since everything goes through the server might simply always get the same reference no matter who called CmdShoot.
You should rather store each players own WeaponMaster reference and go through it like in PlayerUnit
// Already reference this via the inspector (drag&drop)
[SerializeField] private WeaponMaster weaponMaster;
// or as fallback get it on runtime
private void Awake()
{
if(!weaponMaster) weaponMaster = GetComponent<WeaponMaster>();
}
public void Shoot ()
{
if(!isLocalPlayer) return;
CmdShoot();
}
[Command]
private void CmdShoot ()
{
RpcShoot();
}
// Personally in general I would leave any RPC
// as close to the method calling it
// as possible for better maintainability
[ClientRpc]
private void RpcShoot ()
{
weaponMaster.Shoot();
}
and accordingly in WeaponMaster
public void Shoot()
{
if (ammo == 0) return;
if (isAutomatic)
{
keepFiring = true;
StartCoroutine(Fire());
}
else if (!isAutomatic && noQuickFire)
StartCoroutine(Fire());
}
I have two objects in my scene, one which only has a BoxCollider2D (the Column class) and the second object has a Rigibody2D as well as its own BoxCollider2D (Player class).
I added a script to the first object to have an OnCollisionEnter2D. I see it gets triggered when my second object collides with it, and it bounces my 2nd object back when it tries to enter.
I do see my OnCollisionEnter2D method getting called. But if I move my 2nd object again to my first object it gets bounced back again, however I don't see my OnCollisionEnter2D method getting called the 2nd time.
Is this intended behavior? If so, what method would get called every time a collision occurs between these two objects?
Note: I saw OnCollisionStay2D get called a few more times and then it stopped. I assume this is when it's bouncing my 2nd object out. I also see that OnCollisionExit2D never got called. I zoomed into the editor and saw clearly the green lines of the BoxCollider2D did not overlap between my objects so it should've exited the collision when it bounces it back.
public class Column : MonoBehaviour
{
private BoxCollider2D columnColl;
// Start is called before the first frame update
void Start()
{
columnColl = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
}
private void OnCollisionExit2D(Collision2D collision)
{
Debug.Log("Collision Exit");
}
void OnCollisionStay2D(Collision2D collision)
{
//Debug.Log("Collision Stayed");
}
void OnCollisionEnter2D(Collision2D collision)
{
Debug.Log("Collision Happened");
}
}
and
public class Player : MonoBehaviour
{
public float xMoveSpeed = 1f;
private Rigidbody2D rbody;
// Start is called before the first frame update
void Start()
{
rbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("d"))
{
rbody.position = rbody.position + new Vector2(xMoveSpeed, 0);
}
}
}
You shouldn't "manually" change rbody.position.
Rather use Rigidbody.MovePosition
Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.
Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
You also should rather do it in FixedUpdate which is used for the Physics instead of Update
void FixedUpdate()
{
if (Input.GetKeyDown("d"))
{
rbody.MovePosition(rbody.position + new Vector2(xMoveSpeed, 0);
}
}
Also make sure at least one of the objects is not kinematic as to OnCollisionEnter:
Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached.
I've searched around and I just can't get this to work. I think I just don't know the proper syntax, or just doesn't quite grasp the context.
I have a BombDrop script that holds a public int. I got this to work with public static, but Someone said that that is a really bad programming habit and that I should learn encapsulation. Here is what I wrote:
BombDrop script:
<!-- language: c# -->
public class BombDrop : MonoBehaviour {
public GameObject BombPrefab;
//Bombs that the player can drop
public int maxBombs = 1;
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Space)){
if(maxBombs > 0){
DropBomb();
//telling in console current bombs
Debug.Log("maxBombs = " + maxBombs);
}
}
}
void DropBomb(){
// remove one bomb from the current maxBombs
maxBombs -= 1;
// spawn bomb prefab
Vector2 pos = transform.position;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
Instantiate(BombPrefab, pos, Quaternion.identity);
}
}
So I want the Bomb script that's attached to the prefabgameobject Bombprefab to access the maxBombs integer in BombDrop, so that when the bomb is destroyed it adds + one to maxBombs in BombDrop.
And this is the Bomb script that needs the reference.
public class Bomb : MonoBehaviour {
// Time after which the bomb explodes
float time = 3.0f;
// Explosion Prefab
public GameObject explosion;
BoxCollider2D collider;
private BombDrop BombDropScript;
void Awake (){
BombDropScript = GetComponent<BombDrop> ();
}
void Start () {
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void OnTriggerExit2D(Collider2D other){
collider.isTrigger = false;
}
void Explode() {
// Remove Bomb from game
Destroy(gameObject);
// When bomb is destroyed add 1 to the max
// number of bombs you can drop simultaneously .
BombDropScript.maxBombs += 1;
// Spawn Explosion
Instantiate(explosion,
transform.position,
Quaternion.identity);
In the documentation it says that it should be something like
BombDropScript = otherGameObject.GetComponent<BombDrop>();
But that doesn't work. Maybe I just don't understand the syntax here. Is it suppose to say otherGameObject? Cause that doesn't do anything. I still get the error : "Object reference not set do an instance of an object" on my BombDropScript.maxBombs down in the explode()
You need to find the GameObject that contains the script Component that you plan to get a reference to. Make sure the GameObject is already in the scene, or Find will return null.
GameObject g = GameObject.Find("GameObject Name");
Then you can grab the script:
BombDrop bScript = g.GetComponent<BombDrop>();
Then you can access the variables and functions of the Script.
bScript.foo();
I just realized that I answered a very similar question the other day, check here:
Don't know how to get enemy's health
I'll expand a bit on your question since I already answered it.
What your code is doing is saying "Look within my GameObject for a BombDropScript, most of the time the script won't be attached to the same GameObject.
Also use a setter and getter for maxBombs.
public class BombDrop : MonoBehaviour
{
public void setMaxBombs(int amount)
{
maxBombs += amount;
}
public int getMaxBombs()
{
return maxBombs;
}
}
use it in start instead of awake and dont use Destroy(gameObject); you are destroying your game Object then you want something from it
void Start () {
BombDropScript =gameObject.GetComponent<BombDrop> ();
collider = gameObject.GetComponent<BoxCollider2D> ();
// Call the Explode function after a few seconds
Invoke("Explode", time);
}
void Explode() {
//..
//..
//at last
Destroy(gameObject);
}
if you want to access a script in another gameObject you should assign the game object via inspector and access it like that
public gameObject another;
void Start () {
BombDropScript =another.GetComponent<BombDrop> ();
}
Can Use this :
entBombDropScript.maxBombs += 1;
Before :
Destroy(gameObject);
I just want to say that you can increase the maxBombs value before Destroying the game object. it is necessary because, if you destroy game object first and then increases the value so at that time the reference of your script BombDropScript will be gone and you can not modify the value's in it.
I am coding a tower defense game in Unity, and I've ran into a snag while trying to figure out a way to place towers. My idea is to be able to click an art asset in the game when the player has a certain amount of points, and it replaces that art asset with a tower. Unfortunately, even when the player has the right amount of points, the object does not instantiate. I have made sure to link the prefab to the script, but it doesn't work. I'm stumped, the logic of the code seems right but maybe someone can help me figure out what's wrong here.
public class PointManager : MonoBehaviour
{
public int pointCount;
public Text pointDisplay;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
pointDisplay.text = "Points: " + pointCount;
}
}
public class PlaceTower: MonoBehaviour
{
public GameObject Tower;
private GameObject firstTower;
int placeCost = 25;
private PointManager pointsGained;
// Start is called before the first frame update
void Start()
{
pointsGained = GameObject.FindGameObjectWithTag("Point").GetComponent<PointManager>();
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
if (pointsGained.pointCount >= placeCost)
{
firstTower = Instantiate(Tower, transform.position, Quaternion.identity);
//Destroy(this.gameObject);
}
}
}
I got it. The problem was not with my code, but with my prefabs. In the documentation, I misread that OnMouseDown works on objects with colliders. While I had set up a circle collider on the object I was trying to instantiate, I had failed to put one on the object I was trying to instantiate from. Doing so fixed the problem immediately. A simple mistake that I would have completely glanced over had it not been for a second opinion. Thank you, Pac0!
I have an NPC that my player can talk to when the players collider is colliding with the NPC, I do that using this piece of code:
private void OnTriggerStay2D(Collider2D other)
{
if (other.gameObject.tag == "InteractiveArea")
{
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("PRESSED NPC");
CreateAndShowDialog();
}
}
}
However, this gets called really randomly, sometimes the first time I press "E", sometimes the second or third time, etc.
My rigidbodies:
The colliders I use are standard BoxCollider2D, my players collider is a trigger, the NPCs is not.
Why are some key press not detected in the OnTriggerStay function?
OnTriggerStay2D is called randomly. This is why you should never check for Input inside of it.
Set to a flag to true and false in the OnTriggerEnter2D and OnTriggerExit2D functions then check for that flag and input in the Update function which is called every frame. Also, always use CompareTag instead of other.gameObject.tag to compare tags.
private void Update()
{
if (Input.GetKeyDown(KeyCode.E) && triggerStay)
{
//
}
}
bool triggerStay = false;
void OnTriggerEnter2D(Collider2D collision)
{
Debug.Log("Entered");
if (collision.gameObject.CompareTag("InteractiveArea"))
{
triggerStay = true;
}
}
void OnTriggerExit2D(Collider2D collision)
{
Debug.Log("Exited");
if (collision.gameObject.CompareTag("InteractiveArea"))
{
triggerStay = false;
}
}
My guess would be because the value returned by Input.GetKeyDown is only true for the single (Update()) frame that the key was pressed, whereas physics (including OnTriggerStay) are called during physics frames, i.e. FixedUpdate()
Doing a bit of research I pull up this question which suggests:
I've been struggling with this issue all afternoon!
My OnTriggerStay and Update/FixedUpdate methods were not in sync resulting is split-second undesired effects.
I finally found the solution when I read in the OnTriggerStay docs that this function can be a co-routine. I simply added a WaitForFixedUpdate in the correct location in my OnTriggerStay and it worked. They now both run in sync with each other. This even corrects the issue in Update.
Another question has this as a solution (Programmer's answer):
OnTriggerStay will not call on every frame. One way to get around this is to have OnTriggerEnter, and OnTriggerExit set a bool. Then execute your code in the FixedUpdate().
While the documentation no longer says what this post says,
OnTriggerStay gets called every FixedUpdate.
"Note: OnTriggerStay function is on the physics timer so it wont necessary run every frame. "
That confirms my guess and that the documentation was changed to no longer include this note, for some unexplained reason.
You can do this :
bool eIsOnclick;
float wait = 0.03f;
float nextFire = 0.0f;
void Update() {
if(Input.GetKeyDown(keyKode.E))
eIsOnclick = true;
else if(nextFire < Time.time)
nextFire = Time.time + wait;
ebas = false;
}
void OnTriggerStay2D(Collider2D collision) {
if(eIsOnclick == true) {
// your cods
}
// not work in alwaays aquestions