I have a setup similar to this mockup in my 3D Unity game.
Each object has a collider as well as a rigid body. I am moving the object using mouse using simple mouse input events
private void OnMouseDown()
{
isSelected = true;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 touchLocation = InputController.Instance.getTapLocation(); // Gets touch/mouse location using Raycast
_transform.position = track.GetPositionOnPath(touchLocation); //Uses vector projection math
}
private void OnMouseUp()
{
if (isSelected)
isSelected = false;
}
The object movement is constrained to a path using Vector projection
public Vector3 GetPositionOnPath(Vector3 touchLocation)
{
//Make the vector for the track u
Vector3 u = end.position - start.position;
//Make the touch location vector from the track start position v
Vector3 v = touchLocation - start.position;
float uv = Vector3.Dot(u, v);
float uMagSqr = Mathf.Pow(u.magnitude, 2);
Vector3 p = (uv / uMagSqr) * u; //Projection Formula (u.v)/(|u|^2) * u
Vector3 finalVector = start.position + p;
//Clamp the final vector to the end.positions of the track
if (start.position.x > end.position.x)
finalVector.x = Mathf.Clamp(finalVector.x, end.position.x, start.position.x);
else
finalVector.x = Mathf.Clamp(finalVector.x, start.position.x, end.position.x);
if (start.position.z > end.position.z)
finalVector.z = Mathf.Clamp(finalVector.z, end.position.z, start.position.z);
else
finalVector.z = Mathf.Clamp(finalVector.z, start.position.z, end.position.z);
return finalVector;
}
I want to make it in such a way that other objects in the way would obstruct the movement of the selected object as shown in the mockup. The solutions I thought of were to increase the weights of the objects so that they won't fly away when they collide, or to keep every object kinematic except the currently selected one. But they both have the same issue that the objects simply slide through each other.
It seems like a simple thing to do, but I can't seem to find any resources to help me out. I would really appreciate some help on how to tackle this problem and implement the mechanic as I intend. Thanks in Advance.
I have yet to find the exact solution I am looking for. But since it is highly discouraged to move the position of a rigid body object, I switched over to a physics-based movement based on information I found on this and this thread. Here is my current implementation:
private void OnMouseDown()
{
isSelected = true;
_rigidbody.isKinematic = false;
}
private void OnMouseDrag()
{
if (!isSelected) return;
Vector3 target = InputController.Instance.getTapLocation();
//_rigidbody.MovePosition(target);
force = _track.GetPositionOnPath(target) - _transform.position;
}
private void OnMouseUp()
{
if (!isSelected) return;
isSelected = false;
_rigidbody.isKinematic = true;
force = Vector3.zero;
}
private void FixedUpdate()
{
_rigidbody.velocity = force;
}
However, this comes at the cost of the movement not being perfectly responsive to the input given by the player as shown in this gif.
This is a fix so I am posting this as an answer but any other better options are much appreciated.
Related
I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}
These past month+ I learned many things by making a game in Unity.I have a lot of fun doing so. But some thing are still confusing me. I'm trying to setup a skill to the character and it goes almost well. When the character is casting the skill, the skill goes behind the character and not in front. So i thought to play with positions and rotations to make it work but still nothing. Worth to mention that the prefab has it's own motion. So my code so far is this. So help would be great and some teaching about the logic behind the skills system would be much appreciated. So take a look:
using UnityEngine;
public class MagicSkill : MonoBehaviour
{
public GameObject hand; // Players right hand
public GameObject fireballPrefab; // Fireball particle
Vector3 fireballPos; // Adding the transform.position from the players hand
Quaternion fireballRot; // Adding the rotation of the players hand
private bool isPressed = false; //Skill button (mobile)
public Animator animator; // Casting spell animation
void Update()
{
fireballPos = hand.transform.position; // Getting the position of the hand for Instatiating
fireballRot.x = hand.transform.rotation.x; // Getting the rotation of the hand for x Axis
fireballRot.y = hand.transform.rotation.y; // Getting the rotation of the hand for y Axis (Here i try to modify the values but still nothing)
fireballRot.z = hand.transform.rotation.z; // Getting the rotation of the hand for z Axis
if (isPressed == true)
{
animator.SetBool("Magic", true);
if (hand.transform.position.y >= 20) // Here I got the exact position of the hand for when to
Instatiate the skill
{
for (int i = 0; i < 1; i++) // For some reason it instatiates too many prefabs (I think it's because it's in Update method)
{
Instantiate(fireballPrefab, fireballPos, Quaternion.Euler(fireballRot.x, fireballRot.y, fireballRot.z));
Invoke("Update", 2.0f); // I'm trying to slow down the pressed button so that it want spawn every frame
}
}
}
else
{
animator.SetBool("Magic", false);
}
}
public void MagicSkills()
{
if (isPressed == false)
{
isPressed = true;
}
else
{
isPressed = false;
}
}
}
After some playing around with the code I managed to find a solution.Here I post the working code for me at least.Maybe it will help someone else too.For this to work properly you must remove the Event Trigger OnPointerUp from your button.Many thanks for the help Guilherme Schaidhauer Castro
using UnityEngine;
public class MagicSkill : MonoBehaviour
{
public GameObject hand; // Players right hand
public GameObject fireballPrefab; // Fireball particle
public Animator animator; // Casting spell animation
Vector3 fireballPos; // Adding the transform.position from the players hand
private bool isPressed = false; //Skill button (mobile)
void Update()
{
fireballPos = hand.transform.position; // Getting the position of the hand for Instatiating
if (isPressed == true)
{
animator.SetBool("Magic", true);
if (hand.transform.position.y >= 20) // Here I got the exact position of the hand for when to Instatiate the skill
{
Instantiate(fireballPrefab, fireballPos, Quaternion.identity);
isPressed = false;
}
}
else
{
animator.SetBool("Magic", false);
}
}
public void MagicSkills()
{
if (isPressed == false)
{
isPressed = true;
}
else
{
isPressed = false;
}
}
}
Keep a reference to the character's transform and use that transform to instantiate the fireball instead of using the rotation from the hand. The rotation of the hand is not changing in relation to the body, so that's why the ball is always going in the same direction.
I'm working on an Augmented Reality app for Android without tracking images/objects. The user stands at a predefined position and virtual objects are placed into the real world. when the user turns around or moves the phone, the objects are fixed at their respective places. I do this by applying the gyroscope data to the camera.
My problem: I want the objects positions to be always fixed to the same places regardless of the users viewing direction when he starts up the app. Right now, on starting the app, the objects are positioned depending on the camera. After that, they are fixed to their places, when the user changes his viewing direction.
I drew an image of what the exact problem is to better elaborate:
I want to know which sensors are relevant to solve this problem. Since Google Maps accurately determines the viewing direction of a user, I assume there are built in sensors to find out in which direction the user is looking in order to apply this information to the camera's rotation at the start.
This is the code I use to apply the phones rotation to the camera (I'm using Unity and C#):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gyrotransform : MonoBehaviour
{
// STATE
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private Transform _rawGyroRotation;
private float _tempSmoothing;
// SETTINGS
[SerializeField] private float _smoothing = 0.1f;
private IEnumerator Start()
{
Input.gyro.enabled = true;
Application.targetFrameRate = 60;
_initialYAngle = transform.eulerAngles.y;
_rawGyroRotation = new GameObject("GyroRaw").transform;
// _rawGyroRotation.parent = Core.Instance.transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
// Wait until gyro is active, then calibrate to reset starting rotation.
yield return new WaitForSeconds(1);
StartCoroutine(CalibrateYAngle());
}
private void Update()
{
ApplyGyroRotation();
ApplyCalibration();
transform.rotation = Quaternion.Slerp(transform.rotation, _rawGyroRotation.rotation, _smoothing);
}
private IEnumerator CalibrateYAngle()
{
_tempSmoothing = _smoothing;
_smoothing = 1;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle; // Offsets the y angle in case it wasn't 0 at edit time.
yield return null;
_smoothing = _tempSmoothing;
}
private void ApplyGyroRotation()
{
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self); // Swap "handedness" of quaternion from gyro.
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World); // Rotate to make sense as a camera pointing out the back of your device.
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y; // Save the angle around y axis for use in calibration.
}
private void ApplyCalibration()
{
_rawGyroRotation.Rotate(0f, -_calibrationYAngle, 0f, Space.World); // Rotates y angle back however much it deviated when calibrationYAngle was saved.
}
public void SetEnabled(bool value)
{
enabled = true;
StartCoroutine(CalibrateYAngle());
}
}
As far as I understand it the Gyroskope returns the rotational difference since it was started.
That's why your objects appear in the direction you are facing during start.
I guess what you rather want might be Compass.magneticHeading at least for setting the correct rotation once at gamestart
// Orient an object to point to magnetic north.
transform.rotation = Quaternion.Euler(0, -Input.compass.magneticHeading, 0);
You could do this once at start on the parent of all the objects you want to show in order to orient them correctly on the GPS north.
im making a car runner game (all objects are in 3d) where in cops are chasing the player
im spawning the cops from 4 different place (i.e left,right,top,bottom)
private int lastSpawnPos;
[SerializeField]
private Transform[] spawnPos;
void SpawnPoliceCar()
{
GameObject policeCar = ObjectPooling.instance.GetPooledObject("PoliceCar");
int r = UnityEngine.Random.Range(0, spawnPos.Length);
while (lastSpawnPos == r)
{
r = UnityEngine.Random.Range(0, spawnPos.Length);
}
Vector3 policeCarPos = policeCar.transform.position;
policeCarPos = new Vector3(spawnPos[r].position.x, 0, spawnPos[r].position.z);
policeCar.SetActive(true);
policeCar.GetComponent<Damage>().DefaultSetting();
lastSpawnPos = r;
currentPoliceCar++;
}
im calling this method in update() & this script is applied to empty game object in the scene. Since this code works perfectly fine
but now i wanted to add arrow indication on the screen where the cops are spawning as well as i wanted to rotate the arrow as per the direction. im spawning 4 cops at a time.
can any 1 help me in this pls im new to this platform & stuck here from long time
When you're spawning cops you can store them in the List<Transform> cops and remove them (if it's supported to destroy or remove cops from the game). So you'll have player's position and all the cops positions.
Using this data you can find the distance from the cop to the player using Vector3.Distance or Vector2.Distance(if you want to ignore the z coordinate of player and cops). Using this methods you can loop throug cops List and find closets/farthest cop from the player (if you want to show not any cop but closest - the most recently spawned, or closest).
After you have found cop gameobject which you want the arrow to point to you can turn the arrow to this gameobject using Transform.LookAt method. Call this method every Update() call and your arrow's rotation will follow selected cop.
Update:
For the farthest cop your code can look smth like that:
private int lastSpawnPos;
[SerializeField]
private Transform[] spawnPos;
//Your arrow
public GameObject Arrow;
//Player
public Transform Player;
//Storing all spawned cops
public List<Transform> Cops;
void SpawnPoliceCar()
{
GameObject policeCar = ObjectPooling.instance.GetPooledObject("PoliceCar");
int r = UnityEngine.Random.Range(0, spawnPos.Length);
//Adding cop to the list when spawned
//TODO: do not forget to remove from the list, when cop is removed (back to the pool)
Cops.Add(policeCar.transform);
while (lastSpawnPos == r)
{
r = UnityEngine.Random.Range(0, spawnPos.Length);
}
Vector3 policeCarPos = policeCar.transform.position;
policeCarPos = new Vector3(spawnPos[r].position.x, 0, spawnPos[r].position.z);
policeCar.SetActive(true);
policeCar.GetComponent<Damage>().DefaultSetting();
lastSpawnPos = r;
currentPoliceCar++;
}
//Call this on update
void PointArrow()
{
Transform farthestCop = null;
float maxDistance = 0.0f;
//Find the farthes cop
foreach (var cop in Cops)
{
var distance = Vector3.Distance(Player.position, cop.position);
if (distance > maxDistance)
{
farthestCop = cop;
maxDistance = distance;
}
}
//If there are no cops - can't point an arrow
if(farthestCop == null) return;
//Point an arrow on the cop
Arrow.transform.LookAt(farthestCop);
}
I can't exactly say that it is what you are looking for without knowing the full game and code. Hope this will help
Im working on a project that I want to create a power up effect whenever the button "Q" is pressed, I have the animation working and the character, I also have the spawning objects around my player that I want to spawn (See Figure below)
My question is how to add different gravity on each rock (spawning object).
Here is the script that I'm currently using.
/* Public Variables Declaration */
public Transform spawn_LocationForSmall;
public Transform spawn_LocationForMedium;
public Transform spawn_LocationForLarge;
public GameObject smallRock_Prefab;
public GameObject mediumRock_Prefab;
public GameObject largeRock_Prefab;
/* Private Variables Declaration */
private GameObject[] smallRocks_List;
private float posX, posY, posZ;
private bool smallCount = false;
private bool mediumCount = false;
private bool largeCount = false;
private bool small_CheckPos = false;
private bool medium_CheckPos = false;
private bool large_CheckPos = false;
void Start() {
//smallRocks_List = GameObject.FindGameObjectsWithTag("smallRock");
Create_Small_Rocks();
Create_Medium_Rocks();
Create_Large_Rocks();
}
private void Create_Small_Rocks(){
for(int i=0; i<=20; i++){
small_CheckPos = false;
posX = this.transform.position.x + Random.Range(-3.0f, 3.0f);
posY = this.transform.position.y + Random.Range(-3.0f, 3.0f);
posZ = this.transform.position.z + Random.Range(-3.0f, 3.0f);
if(posX > 3f && posY > 3f){
small_CheckPos = true;
}
if (small_CheckPos == true) {
Vector3 newPos = new Vector3(posX, posY, posZ);
GameObject createdObject = GameObject.Instantiate(smallRock_Prefab,
newPos, spawn_LocationForSmall.rotation) as GameObject;
createdObject.transform.parent = spawn_LocationForSmall.transform;
}
}
smallCount = true;
}
/* the other two functions are similar to this */
I don't really know if you can change the gravity for each individual, but you can change these things:
Mass:
In the Rigidbody component, there is a "Mass" components at the top. As in the Unity Documentation says: "Higher mass objects push lower mass objects more when colliding. Think of a big truck, hitting a small car." However, it doesn't change how fast an object falls.
Physics Material:
In the Collider components, you should see something called "Material". You can create new physics materials and edit them randomly to make the friction between the rock and the surface higher or lower, and change the bounciness of rocks that way.
Constant Force:
If you want some objects to fall faster, you might want to use this component. I personally never used this before, but it looks great for your problem. You can add a constant force to an object with this component, so if you add some downwards force on your rocks it should help them get down faster.
Please let me know if any of these helped.
Search for Particle Systems :
1) https://docs.unity3d.com/ScriptReference/ParticleSystem.html
2) https://www.youtube.com/watch?v=FEA1wTMJAR0&t=536s
3) https://www.youtube.com/watch?v=xenW67bXTgM
It allows you to upload cool effects or even prefabs as the clone objects (in this case rocks/asteroids). Its also able to control the spawning speed/ amount/ velosity/ (random)size/ physics(gravity)