I am trying to create a Minesweeper + clicker/idle game for practice. For now, I can't get the clicking to work properly. I spawned in all objects through code meaning they are all the same prefabs but at different locations. When I click them the code activates for all of them and all of the tiles change to being open. I am not sure if I'm using the wrong click function here, as I know there is also an OnMouseDown() function instead of Input.GetMouseButtonDown(0) but the only one where the code activates is in the second one.
public class TileClick : MonoBehaviour {
public GameObject openTile;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0)) {
Instantiate(openTile, transform.position, Quaternion.identity);
Object.Destroy(this.gameObject);
}
}
}
That's my clicking detection. While it does detect clicks, as I said before, it detects them for ALL tiles and activates them all. I only want it to activate on one of them. I've seen RayCasting stuff but I really need more explanation on how that thing works (if that's the solution).
If the TileClick script component is attached to your prefab, then for every tile you instantiate, you have a corresponding TileClick component attached.
When you click, the input is detected by all of these blocks simultaneously, and each of them calls Instantiate but the transform.position is the one corresponding to the tile that makes the call. This is why it seems like all the tiles open up simultaneously, because you are, in fact, instantiating an "open" tile on each of them every time you click.
You should keep all your user input code in a separate MonoBehaviour, and attach that to a single empty GameObject in your scene. For detecting the hit, you should attach a 2DCollider component to your tile prefab (you can decide the type according to the shape of your tiles), and then just use a simple RayCast in your user input code to instantiate an "open" tile at the position where the hit occurs:
void Update () {
if (Input.GetMouseButtonDown(0)) {
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition),
Vector2.zero);
if(hit.collider != null)
{
Instantiate(openTile,
hit.collider.gameObject.transform.position,
Quaternion.identity);
}
}
I highly recommend understanding how RayCast works since it is a very useful tool. Here's a good place to start: https://docs.unity3d.com/ScriptReference/Physics2D.Raycast.html
Related
using UnityEngine;
public class Shoot: MonoBehaviour
{
public Camera cam;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
shooot();
}
}
void shooot()
{
RaycastHit hit;
if (Physics.Raycast(cam.transform.position, cam.transform.forward, out hit))
{
Debug.Log(hit.transform.name);
}
}
The other objects have a box collider and Rigid body. The Raycast detects "Cube1" and after I shoot a raycast at something else and then again shoot a raycast at "Cube1" this code does not detect it. Why?
Unity does log everything. It's just that if you log the same output multiple times Unity collapses into one single line. You can see this by the number indicated on the right side of the output.
(Image taken from https://learn.unity.com/tutorial/introduction-to-the-console-window#5f68b4eeedbc2a002022b83d)
If you toggle the marked "Collapse" button, it will show every single output.
Press Collapse in the Console of the Unity Editor to toggle wherever prints and logs should be stacked (collapsed) or not, it should still log twice, but instead of appearing as two separate logs the counter on the right increases showing how many times the same equal line have been logged.
Here's the code I'm having problems with:
public class PlaneBehaviour : MonoBehaviour {
public GameObject ClickSymbol;
public void Update() {
if (Input.GetMouseButtonDown(0)) {
Instantiate(ClickSymbol, Input.mousePosition, Quaternion.identity);
}
}
}
When user clicks the left mouse button, then multiple ClickSymbol's will be instantiated. How to prevent that behavior (create the object only once) ?
As for the PlaneBehaviour script, it's attached to multiple (not
intersecting, not overlapping) objects on a plane
That's what I thought you are doing and wanted to verify this before adding an answer. Joe's answer should work but this is a bad design.
You get the multiple Objects instead of one because the script is attached to multiple Objects.
The proper solution here is to have one script that reads from the Input then call a function or trigger an event in another script if required. Create a new script called InputReader or with similar name then move your if (Input.GetMouseButtonDown(0))... Make that that this new script is only attached to one GameObject. Just attach it to an empty GameObject. That's it.
It will prevent problems such as executing code twice when there is an input event.
Now, let;s say that you want to notify another script attached to another GameObject when key is pressed, you can find that GameObject, get that script that is attached to it then call its function.
GameObject obj = GameObject.Find("YourOtherObjectName");
Script script = obj.GetComponent<Script >();
script.notifyKeyPress();
I attached it to multiple game objects to be able to detect which game
object was clicked
That's not how to detect which GameObject is clicked. Since this is a plane, I assume it is a 3D Object with a 3D collider. See 6.For 3D Object (Mesh Renderer/any 3D Collider) from my other post on how to do this.
I have a plane that contains around 200 tiles, and I'd like to be able to detect which tile has been clicked by the player. Each tile has a box collider attached. I've also created an empty game object in the scene to which I attached EventSystem and the below script:
public class PlaneBehaviour : MonoBehaviour, IPointerDownHandler {
public GameObject ClickSymbol;
public void Start() {
var physicsRaycaster = FindObjectOfType<PhysicsRaycaster>();
if (physicsRaycaster == null) {
Camera.main.gameObject.AddComponent<PhysicsRaycaster>();
}
}
public void OnPointerDown(PointerEventData eventData) {
var o = eventData.pointerCurrentRaycast.gameObject;
}
}
Now, when user clicks anywhere, nothing happens - I've put a breakpoint inside the OnPointerDown method, but it's not getting hit (and the code inside the Start method is run - I've also verified that by putting a breakpoint in there).
There are many things that could cause the callback function not being called. Before trying these, put Debug.Log inside the OnPointerDown function to make sure that it's being called:
1.Go to GameObject-->UI--->EventSystem.
A GameObject named "EventSystem" will be created and will proper scripts such as EventSystem, StandAloneInputModule attached to it.
2.The PlaneBehaviour script must be attached to the GameObject with the Collider not to an empty GameObject. It detects click on the Objects it is attached to.
3.If you are using a collider that ends with "2D" in its name then Physics2DRaycaster must be used instead of PhysicsRaycaster.
4.Are you using more than two cameras? If so manually attach PhysicsRaycaster to the correct Camera that is currently showing that Object.
5.Re-size the GameObjects you want to detect clicks on to be bigger. I've seen doing this solve the problems sometimes. You then have to move the camera back if this works.
I have two scripts like this:
void Update()
{
if (Input.GetMouseButton(0))
{
if (GameObject.FindGameObjectWithTag("Object").transform.position.x > -1.9)
{
GameObject.FindGameObjectWithTag("Object").transform.Translate(Vector2.left * szybkosc);
}
}
}
And second script is similiar, only there is "Vector2.right" difference. If I have one script, it works perfectly, but when I add second script, they doesn't work. I add these scripts to two different gameobjects.
And second script is similiar, only there is "Vector2.right"
difference
That's the problem right there.
You are moving the object left with ("Object").transform.Translate(Vector2.left * szybkosc);
then you have another script that is moving it right with ("Object").transform.Translate(Vector2.right * szybkosc);.
What do you expect to happen? I think that the "Object" GameObject should remain still or even create a weird behavior. You can't be moving one Object to two opposite directions at the-same time.
Maybe the second script is supposed to be moving another GameObject GameObject.FindGameObjectWithTag("AnotherObject") instead of the first GameObject the first script is already moving....
Not related to your problem but you should call GameObject.FindGameObjectWithTag once in the Start function.
GameObject objToMove;
void Start()
{
objToMove = GameObject.FindGameObjectWithTag("Object");
}
void Update()
{
if (Input.GetMouseButton(0))
{
if (objToMove.transform.position.x > -1.9)
{
objToMove.transform.Translate(Vector2.left * szybkosc);
}
}
}
EDIT:
I'm making control system. I have two arrows - left arrow and right
arrow. When I touch and hold left arrow, object moves left, when I
touch and hold right arrow, object moves right.
It's good to mention what you are doing in your question. This is what EventSystems is used for. OnPointerClick, OnDrag and OnEndDrag are used to make such things. You can find more about this here.
In fact, you need a Visual Joystick. Use Unity's CrossPlatformInputManager from the Asset store and that should do it. It will save you so much time of having to make your own with OnPointerClick and OnDrag .
The value from CrossPlatformInputManager.GetAxisRaw("Horizontal") can then be used to move your Object left or right.
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.