I have a problem. I'm doing a project in Unity 3D (c#), a 3D worlds editor. My problem is that I want to move multiple objects by selecting them. I managed to move one with my mouse cursor, but for multiple I failed :D
This is my code to move one :
public class ClickAndDrag : MonoBehaviour {
private RaycastHit raycastHit;
private GameObject Gobj;
private float distance;
private Vector3 ObjPosition;
private bool Bobj;
// Use this for initialization
void Start() {
}
// Update is called once per frame
void Update() {
if (Input.GetMouseButton (0)) {
var ray = GetComponent<Camera> ().ScreenPointToRay (Input.mousePosition);
var hit = Physics.Raycast (ray.origin, ray.direction, out raycastHit);
if (hit && !Bobj) {
Gobj = raycastHit.collider.gameObject;
distance = raycastHit.distance;
Debug.Log (Gobj.name);
}
Bobj = true;
ObjPosition = ray.origin + distance * ray.direction;
Gobj.transform.position = new Vector3 (ObjPosition.x, ObjPosition.y, ObjPosition.z);
} else {
Bobj = false;
Gobj = null;
}
}
}
Thanks for your help!
private GameObject Gobj; is a variable for a single GameObject. Reformat it to private List<GameObject> objects; and instead of Gobj.transform.position = new Vector3 (ObjPosition.x, ObjPosition.y, ObjPosition.z) do this:
foreach (GameObject item in objects)
{
item.transform.position = new Vector3 (ObjPosition.x, ObjPosition.y, ObjPosition.z)
}
EDIT: In case you aren't sure about how to manipulate a List, List<T> has a set of built in functions to make it really easy. You can now just call objects.Add(newObject); to add an object, and objects.Remove(oldObject); to remove an object.
Related
I'm making a 2D game and i have spawning potion items in a making potion scene first of all i want to make the item pop up after spown and go like in this video that i recorded from my game:
Spawned Item do not move like the original
what do i do to make the spawned (Cloned) Item move the same as the original potion item?
Secondly, i want to spawn random and more than one item (potion) each time the scene starts how do i do that knowing that i'm using this script that spawns only one object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HerbSpawner : MonoBehaviour
{
//Here, we declare variables.
public GameObject objToSpawn;
public Transform groupTransform;
//public means the var is exposed in the inspector, which is super helpful.
// Start is called before the first frame update
Vector2 spawnPos;
void Start()
{
spawnPos = Camera.main.ViewportToWorldPoint(new Vector2(0.5f, 0.5f));
}
// Update is called once per frame
void Update()
{
//let's also spawn on button press:
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.GetRayIntersection(Camera.main.ScreenPointToRay(Input.mousePosition));
if (hit.collider && hit.collider.CompareTag("Bush"))
{
SpawnIt();
}
}
void SpawnIt()
{
Vector2 spawnPos = Camera.main.ViewportToWorldPoint(new Vector2(0.5f, 0.7f));
Instantiate(objToSpawn, spawnPos, Quaternion.identity, groupTransform);
}
}
}
please let me know if there is anyway to do it spawn multiple objects randomly and make the movement for the items to popup like in the video. This is teh script i used for that:
using System.Collections;
using UnityEngine;
using TMPro;
public class DragNDropItem : MonoBehaviour
{
public string itemName;
private bool dragging;
private Vector2 firstPos;
private Color spriteRenderer;
[SerializeField] private float speed = 3;
[SerializeField] private GameObject boiler;
public TMP_Text itemNameText;
private BoilerScript boilerScript;
public AudioSource drop;
void Awake()
{
// Initial position of the item
firstPos = transform.position;
boiler = GameObject.FindGameObjectWithTag("Boiler");
boilerScript = boiler.GetComponent<BoilerScript>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>().color;
}
void OnMouseDown()
{
dragging = true;
}
void OnMouseUp()
{
dragging = false;
if (Vector2.Distance(transform.position, boiler.transform.position) < 0.7f) // We control the distance between the item and the cauldron without using the collider.
{
itemNameText.text = itemName;
boilerScript.Potion(); // Checks the recipe's completion status each time an item is placed.
spriteRenderer.a = 0f;
gameObject.GetComponent<SpriteRenderer>().color = spriteRenderer;
StartCoroutine(Alpha());
}
else drop.Play(); // If the item is left
}
void Update()
{
if (dragging) // As soon as the item is clicked with the mouse, the item follows the mouse.
{
Vector2 mousePosition = MousePos();
transform.position = Vector2.Lerp(transform.position, mousePosition, speed * Time.deltaTime);
} else if (!dragging && (Vector2)transform.position != firstPos) // As soon as we stop clicking, it takes it back to its original place. While the item is in its original location, we are constantly preventing the code from running.
{
transform.position = Vector2.Lerp(transform.position, firstPos, speed * Time.deltaTime);
}
}
Vector2 MousePos() // position of mouse
{
return Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
private IEnumerator Alpha() // After a second the item becomes visible.
{
spriteRenderer.a = 1f;
yield return new WaitForSeconds(0.6f);
gameObject.GetComponent<SpriteRenderer>().color = spriteRenderer;
}
}
Your colliders aren't set to trigger. My guess is, when a clone is spawned, it collides with the existing potion object.
I am currently working on a 2D isometric game where the player will be able to control different units, and interact through them.
I created a Scriptable Object called UnitType. Thanks to this system I can define an action range and a moving range, that is the maximum distance in cells that the player can move to or interact with.
The problem is I don't know how to implement this through code. This is what I want to achieve, using an action range of 2 cells for the demonstration.
This is the goal
With a friend of mine, we thought about calculating the linear equation of those 4 lines to check if the raycast hit was within them, but it's not working right with negative x.
This is the current system
What would be the best practice ?
Thank you very much for your time and attention,
Mousehit is a Unity GameObject that is the child of the character. Its positions is bound to the center of the tilemap cells thanks to mousePosition that is a script attached to the GameManager.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class Interact : MonoBehaviour
{
[SerializeField] private Sprite upperLeft, upperRight, midLeft, midRight, bottomLeft, bottomMid, bottomRight;
[SerializeField] private GameObject mouseHit;
public UnitType unitType;
private GameObject gameManager;
private CheckMousePosition mousePosition;
private bool isInteracting, isAtGoodDistance;
private SpriteRenderer spriteRenderer;
private Tilemap tilemap;
private RaycastHit2D hit;
//private Transform mouseHit;
void Start()
{
tilemap = GameObject.Find("Tilemap").GetComponent<Tilemap>();
gameManager = GameObject.FindGameObjectWithTag("GameManager");
mousePosition = gameManager.GetComponent<CheckMousePosition>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>();
isInteracting = false;
mouseHit.SetActive(false);
}
// Update is called once per frame
void Update()
{
if (isInteracting)
{
mouseHit.SetActive(true);
mouseHit.transform.position = new Vector3(mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).x, mousePosition.tilemap.GetCellCenterLocal(mousePosition.cellPosition).y, 1);
if (Input.GetMouseButtonDown(0))
{
Interaction();
}
}
if (isInteracting == false)
{
spriteRenderer.sprite = null;
mouseHit.SetActive(false);
}
}
public void InitInteraction()
{
isInteracting = true;
transform.root.GetComponent<ContextualMenu>().CloseContextualMenu();
}
private void Interaction()
{
//When the player interacts, we cast a ray that will determine what he is interacting with.
hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hit.collider != null)
{
if (isInteracting)
{
isInteracting = false; //the player is no longer interacting.
//If the player is interacting with a farmable cell using the worker.
if (transform.root.name == "Worker"
&& hit.collider.GetComponent<FarmableCell>() != null)
{
CheckInteractionDistance();
//hit.collider.GetComponent<FarmableCell>().CheckIfFarmed();
}
}
}
}
private void CheckInteractionDistance()
{
if (mouseHit.transform.localPosition.y <= (-0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (-0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x >= (0.5 * (mouseHit.transform.localPosition.x) - unitType.actionDistance / 2)
&& mouseHit.transform.localPosition.x <= (0.5 * (mouseHit.transform.localPosition.x) + unitType.actionDistance / 2))
{
Debug.Log("ok");
}
else
{
Debug.Log("not ok");
}
}
}
I'm trying to get this grabbing script working with my game. In The game you play as a fish in water, and fish should be able to grab and carry objects.
Problem:
I get that with raycast you fire The ray to certain direction which doesn't work for me in this game. This code is otherwise working fine, but I can't figure how to fix the code so that objects can be grabbed from any direction as long as in range.
What I've tried:
I've tried Physics2D.CircleCast, but there also you have to set the certain direction for the cast(?) Also tried OnTriggerEnter but couldn't get that working either. Is there way to cast ray to all directions(not z)? Or is there some more simple way to do this?
using UnityEngine;
using System.Collections;
public class grabberscript : MonoBehaviour
{
public bool grabbed;
RaycastHit2D hit;
public float distance = 0f;
public Transform holdpoint;
public LayerMask notgrabbed;
void Update()
{
if (Input.GetButtonDown("Fire3"))
{
if (!grabbed)
{
Physics2D.queriesStartInColliders = false;
hit = Physics2D.Raycast(transform.position, Vector2.right * transform.localScale.x, distance);
if (hit.collider != null && hit.collider.tag == "grabbable")
{
grabbed = true;
}
}
else
{
grabbed = false;
}
}
if (grabbed)
hit.collider.gameObject.transform.position = holdpoint.position;
}
}
What you are looking for is Physics2D.OverlapCircle .. instead of casting the circle into a certain direction this only checks whether a collider lies within a circle at given center and radius. You the should not check via a tag but rather use a dedicated layer for all objects that shall be able to be grabbed
public class grabberscript : MonoBehaviour
{
public float distance = 0f;
public Transform holdpoint;
// Use the layer(s) for all objects that shall be grab able
public LayerMask grabbable;
private Transform currentlyGrabbedObject;
void Update()
{
if (Input.GetButtonDown("Fire3"))
{
if (!currentlyGrabbedObject)
{
Collider2D hit = Physics2D.OverlapCircle(transform.position, distance, grabbable);
if (hit)
{
currentlyGrabbedObject = hit.transform;
}
}
else // release currently grabbed object
{
currentlyGrabbedObject = null;
}
}
if (currentlyGrabbedObject)
{
currentlyGrabbedObject.position = holdpoint.position;
}
}
}
If you don't use a Layer as said before you would rather have to use Physics2D.OverlapCircleAll and use the first hit matching your tag e.g. using Linq FirstOrDefault like
using System.Linq;
...
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, distance);
Collider2D hit = hits.FirstOrDefault(h => h.CompareTag("grabbale");
if (hit)
{
currentlyGrabbedObject = hit.transform;
}
I think you can use an OverlapCircle to check whether the item to grab is in range or not like this:
if(!grabbing)
{
//This will save the position of all gameObjects in a specific range which are set in a or multiple specific layers
Collider2D[] grabbedItems = Physics2D.OverlapCircleAll(transform.position, grabRange, grabbableLayer);
}
Foreach(Collider2D item in grabbedItems)
{
if(item.ComapreTag("grabbable"))
{
item.isGrabbed = true;
}
}
Then in the gameObject that you intend to make grabbable, you can add a script like this:
public bool isGrabbed = false;
public Transform holdPoint;
void Update()
{
if(isGrabbed)
{
TrackHoldPoint();
}
}
private void TrackHoldPoint()
{
transform.position = holdPoint.position;
}
Then just to be easier to set, you can add this small script to see in edit mode the range of the cirlce:
private void OnDrawGizmosSelected()
{
if (transform == null) return;
Gizmos.DrawWireSphere(transform.position, range);
}
Im working on a first person shooter. I have an aim function, which puts the pistol right in front of the camera, to make it look like your holding it in front of you. Im trying to make it so the pistol will also rotate with the camera on the Z axis, so that way the pistol wont stay still, because that looks odd and gets in the way. To do this, I tried this:
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);, however this ends up rotating the gun very slightly around the z axis, and mainly rotating it around the y axis whenever I move my camera. I am a beginner programmer in Unity so please try to make answers more digestible to beginners so I can understand it. Here is my full script:
using System.Collections.Generic;
using UnityEngine;
public class PistolFire : MonoBehaviour
{
//Gun Properties
public float range = 50f;
public float damage = 10f;
//Sensitivity decrease for looking down the sights
public float downSights = 5f;
//Other vars
private playerGunControls playerGun;
private GameObject plrCam;
private Camera fpsCam;
private ParticleSystem muzzleFlash;
private GameObject impactEffect;
private bool aimed = false;
private GameObject aimPos;
private GunPickupRaycast GPR;
private GameObject handPos;
private GameObject Player;
// Start is called before the first frame update
void Start()
{
//Getting objects because gun is instantiated, so this is necessary
plrCam = GameObject.Find("Player Camera");
playerGun = plrCam.GetComponent<playerGunControls>();
fpsCam = plrCam.GetComponent<Camera>();
muzzleFlash = GetComponentInChildren<ParticleSystem>();
impactEffect = GameObject.Find("Impact Effect");
aimPos = GameObject.Find("aimPos");
GPR = plrCam.GetComponent<GunPickupRaycast>();
handPos = GameObject.Find("Hand Pos");
Player = GameObject.Find("Player");
}
// Update is called once per frame
void Update()
{
//Check for shoot button down
if (Input.GetButtonDown("Fire1"))
{
if (playerGun.holding == "Pistol")
{
Shoot();
}
}
//Check if aim button down
if (Input.GetButton("Fire2"))
{
if (playerGun.holding == "Pistol")
{
Aim();
}
}
//Check if no longer aiming to reset to normal
if (aimed == true && !(Input.GetButton("Fire2")))
{
Unaim();
}
}
void Shoot()
{
muzzleFlash.Play();
RaycastHit hit;
if(Physics.Raycast(plrCam.transform.position, plrCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Health health = hit.transform.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(damage);
}
//Instantiate the Impact Effect
GameObject IE = Instantiate(impactEffect, hit.point, Quaternion.identity);
Destroy(IE, 1.5f);
}
}
void Aim()
{
aimed = true;
Debug.Log("Aiming");
GPR.gun.transform.position = aimPos.transform.position;
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);
}
void Unaim()
{
GPR.gun.transform.position = handPos.transform.position;
Debug.Log("No longer aiming");
aimed = false;
}
}
I fixed my problem by making the gun a child of my camera instead of a child of my player.
On input left click (Fire1), the projectile spawns from the projectileSpawn position. On right click(Fire2), I want to have the projectile shoot "in reverse" and start from the projectileSpawnTwo position and back to the initial projectileSpawn location. It currently shoots forward but I want the projectile to spawn back to an exact position.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerWeapon : MonoBehaviour
{
public GameObject projectilePrefab;
public GameObject projectileTwoPrefab;
public Transform projectileSpawn;
public Transform projectileSpawnTwo;
public float projectileSpeed = 30;
void Update ()
{
if (Input.GetButtonDown("Fire1"))
{
Fire();
}
if (Input.GetButtonDown("Fire2"))
{
Fire2();
}
}
private void Fire()
{
GameObject projectile = Instantiate(projectilePrefab);
Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
projectileSpawn.parent.GetComponent<Collider>());
projectile.transform.position = projectileSpawn.position;
var rot = projectile.transform.rotation.eulerAngles;
projectile.transform.rotation = Quaternion.Euler(rot.x, transform.eulerAngles.y, rot.z);
projectile.GetComponent<Rigidbody>().AddForce(projectileSpawn.forward * projectileSpeed, ForceMode.Impulse);
}
private void Fire2()
{
GameObject projectile = Instantiate(projectileTwoPrefab);
Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
projectileSpawnTwo.parent.GetComponent<Collider>());
projectile.transform.position = projectileSpawnTwo.position;
var rot = projectile.transform.rotation.eulerAngles;
projectile.transform.rotation = Quaternion.Euler(rot.x, transform.eulerAngles.y, rot.z);
projectile.GetComponent<Rigidbody>().AddForce(projectileSpawnTwo.forward * projectileSpeed, ForceMode.Impulse);
}
}
Since both methods are very similar to generalize your method a bit you could do
private Collider projectileSpawnParentCollider;
privtae enum FireType
{
One,
Two
}
private void Awake()
{
// this hould probably done only once to be more efficient
projectileSpawnParentCollider = projectileSpawn.parent.GetComponent<Collider>();
}
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Fire(FireType.One);
}
if (Input.GetButtonDown("Fire2"))
{
Fire(FireType.Two);
}
}
private void Fire(FireType fireType)
{
var position = fireType == FireType.One ? projectileSpawn.position : projectileSpawnTwo.position;
var rot = projectile.transform.rotation.eulerAngles;
var rotation = Quaternion.Euler(rot.x, transform.eulerAngles.y, rot.z);
// pass the position and rotation alraedy
GameObject projectile = Instantiate(projectilePrefab, position, rotation);
Physics.IgnoreCollision(projectile.GetComponent<Collider>(), projectileSpawnParentCollider);
var forceDirection = fireType == FireType.One ? projectileSpawn.forward : (projectileSpawn.position - projectileSpawnTwo.position).normalized;
projectile.GetComponent<Rigidbody>().AddForce(forceDirection * projectileSpeed, ForceMode.Impulse);
}
The
projectileSpawn.position - projectileSpawnTwo.position
returns a Vector pointing from projectileSpawnTwo.position towards projectileSpawn.position. Using normalized returns a vector with the same direction but with magnitude = 1.
However, note that this only works ofcourse if the player is not moving after the second shoot. If the player can move you should probably rather implement another component for making the bullet object actually follow the player position.
You could use Quaternion.LookRotation and Rigidbody.MoveRotation to write a separate component like e.g.
public class FollowPlayer : MonoBehaviour
{
public Transform PlayerTransform;
public RigidBody rigiBody;
public speed;
private void Awake()
{
rigidBody = GetComponent<RigidBody>();
}
private void LateUpdate()
{
if(!PlayerTrasnform) return;
var rotation = Quaternion.LookRotation(PlayerTransform.position - transform.position, Vector3.up);
rigidBody.MoveRotation(rotation);
rigidBody.velocity = (PlayerTransform.position - transform.position).normalized * speed;
}
}
and in your script add the component in case two like
private void Fire(FireType fireType)
{
var position = fireType == FireType.One ? projectileSpawn.position : projectileSpawnTwo.position;
var rot = projectile.transform.rotation.eulerAngles;
var rotation = Quaternion.Euler(rot.x, transform.eulerAngles.y, rot.z);
// pass the position and rotation alraedy
GameObject projectile = Instantiate(projectilePrefab, position, rotation);
Physics.IgnoreCollision(projectile.GetComponent<Collider>(), projectileSpawnParentCollider);
if(fireType == FireType.One)
{
projectile.GetComponent<Rigidbody>().AddForce(projectileSpawn.forward * projectileSpeed, ForceMode.Impulse);
}
else
{
var follow = projectile.AddComponent<FollowPlayer>();
follow.speed = projectileSpeed;
follow.PlayerTransform = YourPlayerTransform; // or probably projectileSpawn
}
}
Note: Typed on smartphone so no warrenty but I hope the idea gets clear.