hello guys i have a code that clone an object on the touched area on game and i have a button that change the rotation of the object (the y rotation by 25 deg),i want when i touch the screen that the clone objects will also change by the current rotation of the object. thank you guys
private Rigidbody myrigidbody;
void Start()
{
myrigidbody = GetComponent<Rigidbody>();
}
void Update()
{
if (Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
var pos = Camera.main.ScreenToWorldPoint(new Vector3(touch.position.x, touch.position.y, 10f));
myrigidbody.MovePosition(new Vector3(pos.x, pos.y, 0f));
}
}
}
Gonna follow up on TEEBQNE here,
you need a way to reference all cubes if you want all of them to rotate. If you are just testing right now, you can tag all cube objects with a certain name like "cube" and use FindGameObjectsWithTag() function to iterate through all of the object types.
So you can change your function from this
public void firstAngle() {
cube.transform.Rotate(0f, 22.5f, 0.0f);
}
To this
public void firstAngle() {
foreach (GameObject obj in FindGameObjectsWithTag("cube")){
obj.transform.Rotate(0f, 22.5f, 0.0f);
}
}
Of course if you want better conventions as suggested in the comments, you can just replace the FindGameObjectsWithTag with a reference to all the cubes in the scene. This can either be an array, list, etc
Related
I make game with isometric view. When player comes into the house the roof of it hides and player can interact with NPCs, items, etc. But now it can interact with it even when roof is visible. How to detect that item is hidden by house roof or wall or another object?
void Update() {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
if (Input.GetMouseButtonDown(0)) {
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
}
//Interact ...
}
}
}
You can simply check the distance between the hit wall/roof and NPC, from the ray-cast origin (camera). Like so:
private Camera cameraRef;
private void Awake() {
// P.S: Cache the 'Camera.main', calls to it can be expensive.
cameraRef = Camera.main;
}
void Update() {
if (Input.GetMouseButtonDown(0)) {
Ray ray = cameraRef.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);
foreach (RaycastHit hit in hits) {
if (hit.collider.tag != "NPC") {
continue;
} else if (RaycastHitRoofOrWallFirst(hits, hit.collider.gameObject)) {
// This NPC is hidden behind a roof/wall.
continue;
}
// Interaction...
}
}
}
/// <summary>
/// Check if a target object is being hidden behind a roof/wall.
/// </summary>
/// <param name="hits">The hits that the raycast gotten.</param>
/// <param name="targetObject">The gameobject to check against.</param>
/// <returns>Return true if the target object is hidden, false if not.</returns>
private bool RaycastHitRoofOrWallFirst(RaycastHit[] hits, GameObject targetObject) {
foreach (RaycastHit hit in hits) {
if (hit.collider.CompareTag("roof") || hit.collider.CompareTag("wall")) {
float distanceFromCameraToObstacle = Vector3.Distance(cameraRef.transform.position, hit.collider.transform.position);
float distanceFromCameraToNPC = Vector3.Distance(cameraRef.transform.position, targetObject.transform.position);
// Check if the NPC is closer to the camera (raycast origin)
// compared to the roof or wall.
if (distanceFromCameraToObstacle < distanceFromCameraToNPC) {
// The roof/wall is closer to the camera (raycast origin)
// compared to the NPC, hence the NPC is blocked by the roof/wall
return true;
}
}
}
return false;
}
Here is a small visual diagram of what it should check for:
Or just use simple raycast...
If possible depending on the context, instead of using Physics.RaycastAll, you can use Physics.Raycast.
It returns the first object that the ray-cast hits.
Adding to this answer an alternative could maybe also be using OnBecameVisible
OnBecameVisible is called when the object became visible by any Camera.
This message is sent to all scripts attached to the Renderer.
and OnBecameInvisible
OnBecameInvisible is called when the Renderer is no longer visible by any Camera.
This message is sent to all scripts attached to the Renderer.
OnBecameVisible and OnBecameInvisible are useful to avoid computations that are only necessary when the object is visible.
For activating and deactivating the according NPC's colliders so the Raycast anyway will only work on visible objects in the first place.
Like on the NPCs have a script
public class InteractableController : MonoBehaviour
{
// you can also reference them via the Inspector
public Collider[] colliders;
private void Awake()
{
// pass in true to also get inactive components
if(colliders.Length = 0) colliders = GetComponentsInChildren<Collider>(true);
}
private void OnBecameInvisible()
{
foreach(var collider in colliders)
{
collider.enabled = false;
}
}
private void OnBecameVisible()
{
foreach(var collider in colliders)
{
collider.enabled = true;
}
}
}
However
Note that object is considered visible when it needs to be rendered in the Scene. It might not be actually visible by any camera, but still need to be rendered for shadows for example. Also, when running in the editor, the Scene view cameras will also cause this function to be called.
I'm currently making a game in Unity where I'm trying to get destroy the clones of a prefab only after they leave the view of the camera only after they already entered the view of the camera in the first place. However, for some reason, my code is instantly destroying the clones as soon as their instantiated. Does anyone know how I could solve this problem?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractControl : MonoBehaviour
{
Rigidbody2D rb;
GameObject target;
float moveSpeed;
Vector3 directionToTarget;
// Use this for initialization
void Start()
{
target = GameObject.Find("White Ball");
rb = GetComponent<Rigidbody2D>();
moveSpeed = 3f;
}
// Update is called once per frame
void Update()
{
MoveInteract();
OnBecameVisible();
}
/*void OnTriggerEnter2D(Collider2D col)
{
switch (col.gameObject.tag)
{
case "ColouredBall Highress":
BallSpawnerControl.spawnAllowed = false;
Destroy(gameObject);
target = null;
break;
case "Star":
Collision collision = new Collision();
break;
}
} */
void MoveInteract()
{
if (target != null)
{
if(ScoreScript.scoreValue > 3)
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
}
else
{
directionToTarget = new Vector3(0, -1, 0);
rb.velocity = new Vector2(0, directionToTarget.y * moveSpeed);
}
}
else
rb.velocity = Vector3.zero;
}
void OnBecameInvisible()
{
if (gameObject.tag == "ColouredBall Highress")
{
Destroy(gameObject);
}
if (gameObject.tag == "Star")
{
Destroy(gameObject);
}
}
void OnBecameVisible()
{
if (gameObject.tag == "ColouredBall Highress" || gameObject.tag == "Star")
{
OnBecameInvisible();
}
}
}
I tried to solve the problem by first requiring the objects to become visible in order for them to be able to get destroyed when out of view of the camera. In short I'm looking for the OnExit collider version for on OnBecameInvisible. I guess I could make the whole screen a collider and use on Exit collider on it. Does someone possibly also know how I could make a collider that covers the camera view?
It is because you call OnBecameInvisible() from OnBecameVisible. So when they are visible they get destroyed.
Also your code is doing so many redundant things you also call OnBecameVisiblefrom Updateetc.
You can simply use this instead:
Renderer m_Renderer;
void Start()
{
m_Renderer = GetComponent<Renderer>();
}
void Update()
{
//It means object is NOT visible in the scene if it is false is visible
if (!m_Renderer.isVisible)
{
Destroy(gameObject);
}
}
Note that: Destroying/Instantiating objects are not the best practices at this circumstance. Because it causes garbage collector to work a lot and it is expensive and can slow down your game. You can use object pooling instead. It basically puts object which are not in the field of view into an object pool and you keep their references and can use them later. Therefore, it is less costly than your method.
You are calling OnBecameVisible every frame, so basically the first frame the object destroys itself. Deleting it from Update should do the trick, Unity already calls it for you.
Is it possible to have two colliders for one object?
My situation is that I have a CircleCollider2D that causes my enemy to chase the player when it enters. This works well but I want to also have a BoxCollider2D that will switch scene to my scene called "BattleScene" when the player enters.
I want it so that when my player enters the circle collider my enemy will follow him but when the player gets closer and enters the box collider (both attached to the enemy) it will switch scenes to the scene called "BattleScene".
Another alternative I thought of was using a rigid body collision but I don't know how to implement that.
Here is my code
private bool checkContact;
private bool checkTrigger;
public float MoveSpeed;
public Transform target;
public Animator anim;
public Rigidbody2D myRigidBody;
BoxCollider2D boxCollider;
public string levelToLoad;
// Start is called before the first frame update
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();//getting the position of our player
anim = GetComponent<Animator>();
myRigidBody = GetComponent<Rigidbody2D>();
boxCollider = gameObject.GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
if (checkTrigger == true)
{
transform.position = Vector2.MoveTowards(transform.position, target.position, MoveSpeed * Time.deltaTime); //move towrds from your position to the position of the player
if (myRigidBody.position.y < target.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x)) //if it is further away from target in x direction than y direction the animation for moving in y is loaded and vice versa
{
anim.SetFloat("MoveY", 1);
anim.SetFloat("MoveX", 0);
}
if (myRigidBody.position.y > target.position.y && Mathf.Abs(target.position.y - myRigidBody.position.y) > Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveY", -1);
anim.SetFloat("MoveX", 0);
}
if (myRigidBody.position.x > target.position.x && Mathf.Abs(target.position.y - myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveX", -1);
anim.SetFloat("MoveY", 0);
}
if (myRigidBody.position.x < target.position.x && Mathf.Abs(target.position.y -myRigidBody.position.y) < Mathf.Abs(target.position.x - myRigidBody.position.x))
{
anim.SetFloat("MoveX", 1);
anim.SetFloat("MoveY", 0);
}
anim.SetBool("checkTrigger", checkTrigger); //updating if in range
}
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
checkTrigger = true; //setting our check trigger = true so it will follow if in radius
anim.SetBool("checkTrigger", checkTrigger);
}
}
public void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.name == "Player")
{
checkTrigger = false; //setting our check trigger = false so it will not follow if not in radius
anim.SetBool("checkTrigger", checkTrigger);
}
EDIT: THIS PROBLEM HAS BEEN RESOLVED
The best way to handle this is by having an empty GameObject with the other collider attached to it, while making sure both GameObjects have a Rigidbody - the child with IsKinematic ticked. Why? Because this will separate the child GameObject from the parent collision structure. Read more about compound colliders.
You should only have more than one collider in one GameObject if they all make part of the same collision structure. If they have different purposes, use different GameObjects with kinematic Rigidbodies, each handling it's own task.
In your specific scenario, I would have the CircleCollider in the enemy itself and the BoxCollider in a child GameObject with a kinematic Rigidbody. This child GameObject can also contain a script with the sole purpose of checking against the Player and loading the BattleScene.
I have a small car game, and when I move up/down and left/right, the sprite becomes different. But the physicsbody remains the same. How do I adjust physicsbody? I added a screenshot of my sprite. At the moment I have Polygon physics body as on the right one.
Here is the code that adjusts animation states:
void FixedUpdate()
{
if (Input.GetKey(KeyCode.W)) {
rb2d.AddForce(Vector2.up * physicsConstant);
animator.CrossFade("CarUpIdle", 0);
} else if (Input.GetKey(KeyCode.S)) {
rb2d.AddForce(-Vector2.up * physicsConstant);
animator.CrossFade("CarDownIdle", 0);
} else if (Input.GetKey(KeyCode.D)) {
rb2d.AddForce(Vector2.right * physicsConstant);
animator.CrossFade("CarRightIdle", 0);
} else if (Input.GetKey(KeyCode.A)) {
rb2d.AddForce(-Vector2.right * physicsConstant);
animator.CrossFade("CarLeftIdle", 0);
}
}
To Change polygon based on sprite first you will need to have Serializefield variables to keep track of all the respective colliders. In this script basically what I do is I am keeping all the polygon colliders in array and iterate the array and enable it depending upon the sprite.
In the script im putting the required sprites along with the respective collider for sprite in sequence. So when I request the sprite to change I enable the respective collider and disables the other colliders. You will require something similer like this :
[SerializeField]
private Sprite[] Sprites;
[SerializeField]
private PolygonCollider2D[] Colliders;
private int index = 0;
private SpriteRenderer sp;
void Start () {
sp = GetComponent<SpriteRenderer>();
sp.sprite = Value[index];
}
void OnGUI() {
if(GUI.Button(new Rect(0,0, 80,35), "ChangeSprite")) {
colliders[index].enabled = false;
index ++;
if(index > Value.Length -1) {
index = 0;
}
sp.sprite = Sprites[index];
colliders[index].enabled = true;
}
}
Also in this tutorial it has been explained how to tackle this kind of problem
Unity Game Tutorial
Another way to proceed is to remove the ploygon collider and recreate it
Destroy(GetComponent<PolygonCollider2D>());
gameObject.AddComponent<PolygonCollider2D>();
though this is very bad programming considering I will be creating collider on every sprite change which is heavy on game.
I have this code here ,i am trying to move an object from one position to another ,on mouse click ,but everytime i run it it justs instantiate the projectile object in a specific position while it had to instantiate it in a the ffor object position
using UnityEngine;
using System.Collections;
public class Shoot : MonoBehaviour
{
public GameObject projectile;
public GameObject foot;
public GameObject mouse;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector2 Target = Input.mousePosition;
Vector2 pos1 = Camera.main.ScreenToWorldPoint(Target);
GameObject newObj = (GameObject)GameObject.Instantiate(projectile);
Vector2 pos2 = foot.transform.position;
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
}
}
There are several issues here, I'll address them one at a time. First, the code for moving the projectile is wired up wrong:
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
You are moving the object that "Shoot" is attached to, not the new game object (newObj, as you have named it.)
Second, it's important to understand the Update pattern and how to properly use it. Update is run every frame. Time.deltaTime is how much time has passed between the last frame render and this one. This number is usually very small. Lastly, Input.GetMouseButtonDown is true only on the first frame that the mouse is pressed.
The current code you have only attempts to (but fails, due to other code problems) move the projectile the one frame the mouse is clicked. What we want is the mouse click to spawn a projectile, and the projectile to move forward EVERY update.
This will be best accomplished with two classes. I will call them Gun and SeekerBullet. The Gun class will be responsible for creating a bullet every time the mouse button is pressed. The SeekerBullet class will be responsible for moving the bullet to it's target.
Gun
public SeekerBullet ProjectilePrefab;
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
FireBullet(target);
}
}
void FireBullet(Vector2 target)
{
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
projectile.GetComponent<SeekerBullet>().Target = target;
}
SeekerBullet
public float MoveSpeed = 5;
public Vector2 Target { get; set; }
public void Update()
{
transform.position = Vector3.MoveTowards(transform.position, Target, MoveSpeed * Time.deltaTime);
if (transform.position == Target)
OnReachTarget();
}
void OnReachTarget()
{
// Do whatever you want here
Destroy(gameObject); // delete this seekerbullet
}
The main Idea I'm trying to stress is isolating your code to perform it's one function well. The gun shouldn't be responsible for moving the projectile, just creating it and telling it where to go.
Also, note that the projectile is starting wherever the Gun is. See this line
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
The transform.position is the position of the gun object. If you want it to start at the foot, like you have in your code, you can re-implement that just like you have in your example.
Why don't you use Raycast. It's pretty simple. Here's an example (Moves the object (transform) in the direction and distance of translation.):
var obj:Transform;
var hit:RaycastHit;
var move: boolean = false;
var moveSpeed:float;
var moveTime:float;
private var startTime:float;
function Update ()
{
if (Input.GetButtonDown ("Fire1"))
{
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, hit, 10000)){
move = true;
startTime = Time.time;
}
}
if(move){
var curTime = Time.time;
var elapsedTime = curTime - startTime;
var amountToMove = elapsedTime / moveTime;
print(amountToMove);
obj.transform.position = Vector3.Lerp(obj.transform.position, hit.point, amountToMove);
if(obj.transform.position == hit.point){
move = false;
}
This code creates a plane, and a cube. Assign the above code to the plane, assign the cube to the obj var in the inspector. Click on the plane, and watch the fun.
This is pretty simple. You can use the Vector3.Lerp function to achieve this. Use raycasting to get the mouse click position or the touch position. Then use the initial and the final position in the lerp function. The initial position being the position that the gameobject is at now and the final position being the click / touch position.
You can find the article by The Game Contriver on the same here
Move to Touch / Click Position - The Game Contriver