Properly attach to a GameObject after collision? - c#

How can I properly make a GameObject attach (or "stick") to another GameObject after collision? The problem: I want the GameObject to attach after collision even if it is changing scale.
"Attach on collision" code:
protected Transform stuckTo = null;
protected Vector3 offset = Vector3.zero;
public void LateUpdate()
{
if (stuckTo != null)
transform.position = stuckTo.position - offset;
}
void OnCollisionEnter(Collision col)
{
rb = GetComponent<Rigidbody>();
rb.isKinematic = true;
if(stuckTo == null
|| stuckTo != col.gameObject.transform)
offset = col.gameObject.transform.position - transform.position;
stuckTo = col.gameObject.transform;
}
This code makes a GameObject attach perfectly after collision. But when that GameObject changes scale (while it's attached), it visually no longer looks attached to whatever it collided with. Basically, this code makes the GameObject stick with only the original scale at the moment of the collision. How can I make the GameObject always stick to whatever it collided with? And with whatever scale it has during the process? I would like to avoid parenting: "It's a bit unsafe though, parenting colliders can cause weird results, like random teleporting or object starting to move and rotate insanely, etc." - Samed Tarık ÇETİN : comment.
Scaling script:
public Transform object1; //this is the object that my future-scaling GameObject collided with.
public Transform object2; //another object, the same scale as object1, somewhere else
//(or vice versa)
void Update ()
{
float distance = Vector3.Distance (object1.position, object2.position);
float original_width = 10;
if (distance <= 10)
{
float scale_x = distance / original_width;
scale_x = Mathf.Min (scale_x, 3.0f);
transform.localScale = new Vector3 (scale_x * 3.0f, 3.0f / scale_x, 3.0f);
}
}

Your basic idea is right, your code can be modified slightly to support this.
Here is the trick: instead instead of sticking your object to the object it collided with, you create a dummy game object, lets call it "glue", at the collision point, and stick your object to the glue. The glue object is then parented to the object we collided with.
Since glue is just a dummy object with only components transform and some script, there is no problem with parenting.
Also, pay attention that it does not really matter at which contact point we create the glue, in case we have multiple contact points, and it is also easy to extend this to support rotations, see below.
So on collision, the only thing we do now is creating a glue. Here is the code:
void CreateGlue(Vector3 position, GameObject other) {
// Here we create a glue object programatically, but you can make a prefab if you want.
// Glue object is a simple transform with Glue.cs script attached.
var glue = (new GameObject("glue")).AddComponent<Glue>();
// We set glue position at the contact point
glue.transform.position = position;
// This also enables us to support object rotation. We initially set glue rotation to the same value
// as our game object rotation. If you don't want rotation - simply remove this.
glue.transform.rotation = transform.rotation;
// We make the object we collided with a parent of glue object
glue.transform.SetParent(other.transform);
// And now we call glue initialization
glue.AttachObject(gameObject);
}
void OnCollisionEnter(Collision col)
{
// On collision we simply create a glue object at any contact point.
CreateGlue(col.contacts[0].point, col.gameObject);
}
And here is how Glue.cs script looks, it will handle LateUpdate and modify transform.
public class Glue : MonoBehaviour {
protected Transform stuckTo = null;
protected Vector3 offset = Vector3.zero;
public void AttachObject(GameObject other)
{
// Basically - same code as yours with slight modifications
// Make rigidbody Kinematic
var rb = other.GetComponent<Rigidbody>();
rb.isKinematic = true;
// Calculate offset - pay attention the direction of the offset is now reverse
// since we attach glue to object and not object to glue. It can be modified to work
// the other way, it just seems more reasonable to set all "glueing" functionality
// at Glue object
offset = transform.position - other.transform.position;
stuckTo = other.transform;
}
public void LateUpdate()
{
if (stuckTo != null) {
// If you don't want to support rotation remove this line
stuckTo.rotation = transform.rotation;
stuckTo.position = transform.position - transform.rotation * offset;
}
}
// Just visualizing the glue point, remove if not needed
void OnDrawGizmos() {
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(transform.position, 0.2f);
}
}
Also, pay attention that simply parenting the objects as it was suggested here will get you in some additional trouble, because scaling parent also scales children, so you will have to re-scale the child back to its original size. The problem is that these scaling operations are relative to different anchor points, so you will also have to make additional adjustments in objects position. Can be done though.
I also created a small sample project, see here (Unity v5.2.f3):
https://www.dropbox.com/s/whr85cmdp1tv7tv/GlueObjects.zip?dl=0
P.S. I see that you mix transform and rigidbody semantics, since it is done on Kinematic rigidbodies it is not a big deal, but just a suggestion: think whether you really need to have rigidbodies on objects that are already "stuck" to others, if not - maybe just remove or disable the rigidbody instead of making it Kinematic.

What you want to do is scale about the collision point. You can achieve this by setting the pivot point to the collision point, that way when you scale the object it will be scaled based on the pivot. The best way to simulate this is using sprites in Unity.
In the above image, the top-left crate has a pivot point in the center, so when you scale it along x, it scales about that point, increasing it's width both sides of the pivot point. But when the pivot point is set to the side, like in the bottom left image, when you scale it, it can only extend it out to the left (unless you scale negatively of course).
Now the problem is you can't easily change the pivot point of mesh, so there are a number of different work-arounds for this. One such approach is to attach the mesh to an empty new gameObject and adjust the mesh's local transform in relation to the parent, simulating moving the meshes pivot point.
What the below code does is determine the collision point. Since there can be multiple collision points, I get the average collision point of them all. I then move the mesh's parent to the collision point and adjust the meshes local position so that the side of the cube is positioned at that point, this acts as setting the mesh's pivot point to the collision point.
Now when you scale, it will scale about the collision point, just like in the above image.
public MeshRenderer _meshRenderer;
public float _moveXDirection;
public Rigidbody _rigidBody;
public Transform _meshTransform;
public bool _sticksToObjects;
public ScalingScript _scalingScript;
protected Transform _stuckTo = null;
protected Vector3 _offset = Vector3.zero;
void LateUpdate()
{
if (_stuckTo != null)
{
transform.position = _stuckTo.position - _offset;
}
}
void OnCollisionEnter(Collision collision)
{
if (!_sticksToObjects) {
return;
}
_rigidBody.isKinematic = true;
// Get the approximate collision point and normal, as there
// may be multipled collision points
Vector3 contactPoint = Vector3.zero;
Vector3 contactNormal = Vector3.zero;
for (int i = 0; i < collision.contacts.Length; i++)
{
contactPoint += collision.contacts[i].point;
contactNormal += collision.contacts[i].normal;
}
// Get the final, approximate, point and normal of collision
contactPoint /= collision.contacts.Length;
contactNormal /= collision.contacts.Length;
// Move object to the collision point
// This acts as setting the pivot point of the cube mesh to the collision point
transform.position = contactPoint;
// Adjust the local position of the cube so it is flush with the pivot point
Vector3 meshLocalPosition = Vector3.zero;
// Move the child so the side is at the collision point.
// A x local position of 0 means the child is centered on the parent,
// a value of 0.5 means it's to the right, and a value of -0.5 means it to the left
meshLocalPosition.x = (0.5f * contactNormal.x);
_meshTransform.localPosition = meshLocalPosition;
if (_stuckTo == null || _stuckTo != collision.gameObject.transform)
{
_offset = collision.gameObject.transform.position - transform.position;
}
_stuckTo = collision.gameObject.transform;
// Enable the scaling script
if (_scalingScript != null)
{
_scalingScript.enabled = true;
}
}
Here is an example project with the above code:
https://www.dropbox.com/s/i6pdlw8mjs2sxcf/CubesAttached.zip?dl=0

change global
protected Collider stuckTo = null;
///// use Collider instead of transform object. You might get better solution.Inform me if it works or gives any error since i haven't tried if it works i would like to know.
void OnCollisionEnter(Collision col)
{
rb = GetComponent<Rigidbody>();
rb.isKinematic = true;
if(stuckTo == null || stuckTo != col.gameObject.transform)
offset = col.collider.bounds.center - transform.position;
stuckTo = col.collider;
}
public void LateUpdate()
{
if (stuckTo != null)
{
Vector3 distance=stuckTo.bounds.extents + GetComponent<Collider>().bounds.extents;
transform.position = stuckTo.bounds.center + distance;
}
}

Make sure that you are scaling the stuckTo transform (the one that has the collider attached to) and not any of it's parents or this will not work.
if the stuckTo's scale is uniform:
protected Transform stuckTo = null;
protected Vector3 originalPositionOffset = Vector3.zero;
protected Vector3 positionOffset = Vector3.zero;
protected Vector3 originalScaleOfTheTarget = Vector3.zero;
public void LateUpdate()
{
if (stuckTo != null){
positionOffset *= stuckTo.localScale.x;
transform.position = stuckTo.position - positionOffset;
}
}
void OnCollisionEnter(Collision col)
{
rb = GetComponent<Rigidbody>();
rb.isKinematic = true;
if(stuckTo == null
|| stuckTo != col.gameObject.transform){
originalScaleOfTheTarget = col.gameObject.transform.localScale;
originalPositionOffset = col.gameObject.transform.position - transform.position;
originalPositionOffset /= originalScaleOfTheTarget.x;
}
stuckTo = col.gameObject.transform;
}
but if the stuckTo's scale is non-uniform:
protected Transform stuckTo = null;
protected Vector3 originalPositionOffset = Vector3.zero;
protected Vector3 positionOffset = Vector3.zero;
protected Vector3 originalScaleOfTheTarget = Vector3.zero;
public void LateUpdate()
{
if (stuckTo != null){
positionOffset.x = originalPositionOffset.x * stuckTo.localScale.x;
positionOffset.y = originalPositionOffset.y * stuckTo.localScale.y;
positionOffset.z = originalPositionOffset.z * stuckTo.localScale.z;
transform.position = stuckTo.position - positionOffset;
}
}
void OnCollisionEnter(Collision col)
{
rb = GetComponent<Rigidbody>();
rb.isKinematic = true;
if(stuckTo == null
|| stuckTo != col.gameObject.transform){
originalScaleOfTheTarget = col.gameObject.transform.localScale;
originalPositionOffset = col.gameObject.transform.position - transform.position;
originalPositionOffset.x /= originalScaleOfTheTarget.x;
originalPositionOffset.y /= originalScaleOfTheTarget.y;
originalPositionOffset.z /= originalScaleOfTheTarget.z;
}
stuckTo = col.gameObject.transform;
}
But still though - why are you following ÇETİN's advice man? It's totally safe to parent colliders and rigidbodies and literally anything as long as you know what you are doing. Just parent your sticky transform under the target and bam! if something goes wrong just remove your rigidbody component or disable your collider component.

Related

Paddle reflect ball depending on contact point

When hitting paddle ball is bouncing weird.
private Vector3 direction;
private void Start()
{
direction = transform.up;
}
private void FixedUpdate()
{
transform.Translate(direction * (Time.deltaTime * BallManager.Instance.initialBallSpeed));
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Paddle"))
{
var cp = col.contacts[0];
direction = Vector2.Reflect(direction, PaddleScript.Instance.transform.position.normalized);
}
}
I want to make to bounce with always same force up and to side depending on hit point.
First of all when using Physics you shouldn't go through Transform at all.
You wouldn't even need this component at all.. why not simply
eliminate all gravity (Gravity Scale -> 0)
eliminate all drag (Linear Drag -> 0)
put a proper PhysicsMaterial2D on the ball and/or paddles colliders and set bounciness to 1 and friction to 0
=> It should already behave as desired by default.
Otherwise I would change your code to
[SerializeField] private Rigidbody2D rigidbody;
private Vector3 direction;
private void Start()
{
if(!rigidbody) rigidbody= GetComponent<Rigidbody2D>();
direction = transform.up;
}
private void FixedUpdate()
{
rigidbody.MovePosition(rigidbody.position + direction * Time.deltaTime * BallManager.Instance.initialBallSpeed);
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Paddle"))
{
var cp = col.contacts[0];
// and then you want to reflect on the normal of the contact..
// a normalized position doesn't make any sense in this context
direction = Vector2.Reflect(direction, cp.normal);
}
or if you wanted to add the variance depending on where the ball hits the paddle I would put an object at each tip of the paddle and then you can inverse lerp
public static class Vector2Extensions
{
public static float InverseLerp(thjs Vector2 value, Vector2 min, Vector2 max)
{
var direction = max - min;
var AV = value - min;
return Mathf.Clamp01(Vector2.Dot(AV, direction) / Vector2.Dot(direction, direction));
}
}
to check whether your hit contact is closer to left (0) or right (1) tip or exactly in the center (0.5) and according to this factor add some min/max angle offset to the vector. e.g. using plain reflect as before when exactly in the center and then add some rotation according to the offset factor from above e.g. using
direction = Vector2.Reflect(direction, cp.normal);
// this will return a dynamic factor of whether the contact is closer to left (0), right (1) or in the middle (0.5)
var inverseLerp = cp.point.InverseLerp(leftTip, rightTip);
// this will map the inverseLerp factor which goes between `0` and `1`
// to the added angle offset which goes from -30° to 30°
var angle = Mathf.Lerp(-30, 30, inverseLerp);
direction = (Vector2)(Quaternion.Euler(0, 0, angle) * direction);
If you want you could even remove the Reflect entirely and then not add a rotation to the direction but rather hard replace it with a new direction based on the same lerp as before

Unity3D FollowCam not following player vehicle

I have a garage that gives the option for the player to select the car he wants to take to the race, the problem is the camera doesn't attach to the when it's sent.
The follow camera is using the standard unity assets.
Garage code(This code loads the car into the map):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class LoadCharacter : MonoBehaviour
{
public GameObject[] characterPrefabs;
public Transform spawnPoint;
//public TMP_Text label;
void Start()
{
int selectedCharacter = PlayerPrefs.GetInt("selectedCharacter");
GameObject prefab = characterPrefabs[selectedCharacter];
GameObject clone = Instantiate(prefab, spawnPoint.position, Quaternion.identity);
//label.text = prefab.name;
}
}
Camera code:
using System;
using UnityEngine;
#if UNITY_EDITOR
#endif
namespace UnityStandardAssets.Cameras
{
[ExecuteInEditMode]
public class AutoCam : PivotBasedCameraRig
{
[SerializeField] private float m_MoveSpeed = 3; // How fast the rig will move to keep up with target's position
[SerializeField] private float m_TurnSpeed = 1; // How fast the rig will turn to keep up with target's rotation
[SerializeField] private float m_RollSpeed = 0.2f;// How fast the rig will roll (around Z axis) to match target's roll.
[SerializeField] private bool m_FollowVelocity = false;// Whether the rig will rotate in the direction of the target's velocity.
[SerializeField] private bool m_FollowTilt = true; // Whether the rig will tilt (around X axis) with the target.
[SerializeField] private float m_SpinTurnLimit = 90;// The threshold beyond which the camera stops following the target's rotation. (used in situations where a car spins out, for example)
[SerializeField] private float m_TargetVelocityLowerLimit = 4f;// the minimum velocity above which the camera turns towards the object's velocity. Below this we use the object's forward direction.
[SerializeField] private float m_SmoothTurnTime = 0.2f; // the smoothing for the camera's rotation
private float m_LastFlatAngle; // The relative angle of the target and the rig from the previous frame.
private float m_CurrentTurnAmount; // How much to turn the camera
private float m_TurnSpeedVelocityChange; // The change in the turn speed velocity
private Vector3 m_RollUp = Vector3.up;// The roll of the camera around the z axis ( generally this will always just be up )
protected override void FollowTarget(float deltaTime)
{
// if no target, or no time passed then we quit early, as there is nothing to do
if (!(deltaTime > 0) || m_Target == null)
{
return;
}
// initialise some vars, we'll be modifying these in a moment
var targetForward = m_Target.forward;
var targetUp = m_Target.up;
if (m_FollowVelocity && Application.isPlaying)
{
// in follow velocity mode, the camera's rotation is aligned towards the object's velocity direction
// but only if the object is traveling faster than a given threshold.
if (targetRigidbody.velocity.magnitude > m_TargetVelocityLowerLimit)
{
// velocity is high enough, so we'll use the target's velocty
targetForward = targetRigidbody.velocity.normalized;
targetUp = Vector3.up;
}
else
{
targetUp = Vector3.up;
}
m_CurrentTurnAmount = Mathf.SmoothDamp(m_CurrentTurnAmount, 1, ref m_TurnSpeedVelocityChange, m_SmoothTurnTime);
}
else
{
// we're in 'follow rotation' mode, where the camera rig's rotation follows the object's rotation.
// This section allows the camera to stop following the target's rotation when the target is spinning too fast.
// eg when a car has been knocked into a spin. The camera will resume following the rotation
// of the target when the target's angular velocity slows below the threshold.
var currentFlatAngle = Mathf.Atan2(targetForward.x, targetForward.z)*Mathf.Rad2Deg;
if (m_SpinTurnLimit > 0)
{
var targetSpinSpeed = Mathf.Abs(Mathf.DeltaAngle(m_LastFlatAngle, currentFlatAngle))/deltaTime;
var desiredTurnAmount = Mathf.InverseLerp(m_SpinTurnLimit, m_SpinTurnLimit*0.75f, targetSpinSpeed);
var turnReactSpeed = (m_CurrentTurnAmount > desiredTurnAmount ? .1f : 1f);
if (Application.isPlaying)
{
m_CurrentTurnAmount = Mathf.SmoothDamp(m_CurrentTurnAmount, desiredTurnAmount,
ref m_TurnSpeedVelocityChange, turnReactSpeed);
}
else
{
// for editor mode, smoothdamp won't work because it uses deltaTime internally
m_CurrentTurnAmount = desiredTurnAmount;
}
}
else
{
m_CurrentTurnAmount = 1;
}
m_LastFlatAngle = currentFlatAngle;
}
// camera position moves towards target position:
transform.position = Vector3.Lerp(transform.position, m_Target.position, deltaTime*m_MoveSpeed);
// camera's rotation is split into two parts, which can have independend speed settings:
// rotating towards the target's forward direction (which encompasses its 'yaw' and 'pitch')
if (!m_FollowTilt)
{
targetForward.y = 0;
if (targetForward.sqrMagnitude < float.Epsilon)
{
targetForward = transform.forward;
}
}
var rollRotation = Quaternion.LookRotation(targetForward, m_RollUp);
// and aligning with the target object's up direction (i.e. its 'roll')
m_RollUp = m_RollSpeed > 0 ? Vector3.Slerp(m_RollUp, targetUp, m_RollSpeed*deltaTime) : Vector3.up;
transform.rotation = Quaternion.Lerp(transform.rotation, rollRotation, m_TurnSpeed*m_CurrentTurnAmount*deltaTime);
}
}
}
This are the cars that the map gets from the selection
This is the follow cam
This is what happens when the player chooses the car, it switches places with an empty object
The only way to follow is if I go in the editor and manually drag the car into the follow
Edit: I found a way to work around it, anyone wondering you just have to make a prefab with the car and camera.

How to make a smooth crosshair in Unity3D?

In Unity 3D I'd like to create a crosshair for my top-down 2D-shooter that gradually moves to its target whenever the player has the same x-position as the target.
The problem is that I want a smooth animation when the crosshair moves to the target. I have included a small gif from another game that shows a crosshair I'd like to achieve. Have a look at it:
Crosshair video
I tried to do that with the following script but failed - the crosshair jumps forth and back when the enemies appear. It doesn't look so smooth like in the video I mentioned above.
The following script is attached to the player:
[SerializeField]
private GameObject crosshairGO;
[SerializeField]
private float speedCrosshair = 100.0f;
private Rigidbody2D crosshairRB;
private bool crosshairBegin = true;
void Start () {
crosshairRB = crosshairGO.GetComponent<Rigidbody2D>();
crosshairBegin = true;
}
void FixedUpdate() {
//Cast a ray straight up from the player
float _size = 12f;
Vector2 _direction = this.transform.up;
RaycastHit2D _hit = Physics2D.Raycast(this.transform.position, _direction, _size);
if (_hit.collider != null && _hit.collider.tag == "EnemyShipTag") {
// We touched something!
Debug.Log("we touched the enemy");
Vector2 _direction2 = (_hit.collider.gameObject.transform.position - crosshairGO.transform.position).normalized;
crosshairRB.velocity = new Vector2(this.transform.position.x, _direction2.y * speedCrosshair);
crosshairBegin = false;
} else {
// Nothing hit
Debug.Log("nothing hit");
crosshairRB.velocity = Vector2.zero;
Vector2 _pos2 = new Vector2(this.transform.position.x, 4.5f);
if (crosshairBegin) crosshairGO.transform.position = _pos2;
}
}
I think you need create a new variable call Speed translation
with
speed = distance from cross hair to enemy position / time (here is Time.fixedDeltaTime);
then multiply speed with velocity, the cross hair will move to enmey positsion in one frame.
but you can adjust speed by mitiply it with some float > 0 and < 1;

Switching between two or more cameras unity3D

I'm making a game in which the player controls two different characters (each one has its own empty object with a camera as child), and switchs one or another by pressing the control key. The thing is, I'm trying to make a little transition between both characters cameras by using another camera, so it doesn't just teleports between one and another but I can't seem to do it. I tried with lerp but I don't know if I got it right, so I read and tried Vector3.MoveTowards but still couldn't do it. This is my code so far (the while is because a last-moment-braindead I had):
public class CameraController : MonoBehaviour
{
public Camera cam1;
public Camera cam2;
public Camera movingCamera;
public bool isCurrentPlayer;
public Transform target1;
public Transform target2;
public float speed = 0.2f;
void FixedUpdate()
{
float step = speed * Time.deltaTime;
if (Input.GetButtonDown("Control"))
{
if (isCurrentPlayer)
{
movingCamera.enabled = true;
cam2.enabled = false;
while (transform.position != target1.position)
{
transform.position = Vector3.MoveTowards(transform.position, target1.position, step);
}
if (transform.position == target1.transform.position)
{
movingCamera.enabled = false;
cam1.enabled = true;
}
isCurrentPlayer = false;
}
else if (!isCurrentPlayer)
{
movingCamera.enabled = true;
cam1.enabled = false;
while (transform.position != target2.position)
{
transform.position = Vector3.MoveTowards(transform.position, target2.position, step);
}
if (transform.position == target2.transform.position)
{
movingCamera.enabled = false;
cam2.enabled = true;
}
isCurrentPlayer = true;
}
}
}
I'm curious about two things. Why did you use FixedUpdate to manage your updates? This isn't physics code. Is there a particular reason you are using multiple cameras? If I may, I propose the following changes.
You can simply make use of the main camera instead of multiple cameras. Additionally, you can increase the number of player objects you can toggle through by using an array of player GameObjects, and by changing the input parameters to left control and right control, you can toggle between next player and previous player to navigate bi-directionally through the array of players.
Here's my example code that implements these changes (tested and works, though improvements can be made.)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Attached to Main Camera
public class CameraController : MonoBehaviour {
// set manually in inspector
public GameObject[] players;
public float movementSpeed = 1.0f;
public float rotationSpeed = 1.0f;
private int currentPlayer;
private float startTime;
private float distanceToPlayer;
private Vector3 startPosition;
private Quaternion startOrientation;
// Use this for initialization
void Start () {
currentPlayer = 0;
ResetCamera();
}
// Update is called once per frame
void Update () {
float distanceCovered;
float rotationCovered;
float fractionTraveled;
// switch to previous
if (Input.GetButtonDown("left ctrl")) {
if (currentPlayer == 0) currentPlayer = players.Length - 1;
else currentPlayer--;
ResetCamera();
}
// switch to nextPlayer
if (Input.GetButtonDown("right ctrl")) {
if (currentPlayer == players.Length - 1) currentPlayer = 0;
else currentPlayer++;
ResetCamera();
}
// Keep moving camera
if (transform.position != players[currentPlayer].transform.position)
{
distanceCovered = (Time.time - startTime) * movementSpeed;
fractionTraveled = distanceCovered / distanceToPlayer;
rotationCovered = (Time.time - startTime) * rotationSpeed;
// Lerp to player position
transform.position = Vector3.Lerp(
startPosition,
players[currentPlayer].transform.position,
fractionTraveled
);
// match player orientation
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
players[currentPlayer].transform.rotation,
rotationCovered
);
// Stop moving camera
} else {
// Match orientation
if (transform.rotation != players[currentPlayer].transform.rotation)
transform.rotation = players[currentPlayer].transform.rotation;
// Set parent transform to current player
transform.parent = players[currentPlayer].transform;
}
}
void ResetCamera() {
transform.parent = null;
startTime = Time.time;
startPosition = transform.position;
startOrientation = transform.rotation;
distanceToPlayer = Vector3.Distance(
transform.position,
players[currentPlayer].transform.position
);
}
}
Obviously the values would need to be tweaked, and the movement algorithm is pretty basic. Crude but function. You can also add player movement code into the camera, just make sure it points to players[currentPlayer] and it will work for each of your player objects without having to use additional scripts unless there is a reason to do so.
Feel free to use this code. Like I said, it works. However, should you choose to do so, it can easily be modified to function like your original code simply by removing the array and reinstating the individual GameObjects.
transform.position points to the position of CameraController game object. If you want to move movingCamera you probably want to use movingCamera.transform.position. Also keep in mind that in your script the MoveTowards() would only fire when you are pressing your "Control" button.
As memBrain said it would be the best practice to use only one camera for this - visually it would look the same.
The script should look something like this:
// Assuming target1 is player 1 and target2 is player 2
private float snapThreshold = 0.1f;
private Vector3 movingCameraDestination = Vector3.zero;
void FixedUpdate()
{
if(Input.GetButtonDown("Control"))
{
if(isCurrentPlayer)
{
//Set position of transition camera to player 1 and set it's destination to player's 2 position
movingCamera.transform.position = player1.position;
movingCameraDestination = player2.position;
//Disable player 1 camera and enable transition camera
cam1.enabled = false;
movingCamera.enabled = true;
}
else
{
//Set position of transition camera to player 21 and set it's destination to player's 1 position
movingCamera.transform.position = player2.position;
movingCameraDestination = player1.position;
//Disable player 1 camera and enable transition camera
cam2.enabled = false;
movingCamera.enabled = true;
}
}
//If transition camera is enabled and its destination is not Vector3.zero - move it
if(movingCameraDestination != Vector3.zero && movingCamera.enabled)
{
movingCamera.transform.position = Vector3.Lerp(movingCamera.transform.position, movingCameraDestination, speed * Time.deltaTime);
//If the distance between transition camera and it's destination is smaller or equal to threshold - snap it to destination position
if(Vector3.Distance(movingCamera.transform.position, movingCameraDestination) <= snapThreshold)
{
movingCamera.transform.position = movingCameraDestination;
}
//If transition camera reached it's destination set it's destination to Vector3.zero and disable it
if(movingCamera.transform.position == movingCameraDestination)
{
movingCameraDestination = Vector3.zero;
movingCamera.enabled = false;
}
}
}

Unity3D on Click ignore all colliders accept the one on specific layer

I'm trying to make a simple drag and drop game where you have objects that can be picked up and if left above 1/2 of the screens height then it will fall down to high they will fall if you let them go below half of the screen they will stay there. Its a 2D game and what give the illusion of depth.
This is an image of the Thing i would like to make
Currently I can move the blue bucket from the bottom. If i lift the bucket above the PolygonCollider it will fall down to the Edge Collider 4. When i leave it somewhere withing the Polygone Colider i set the object to kinetic so it wont fall it will give the illusion that you placed it on the ground.
My problem is that the colliders of the bucket what I use to detect the click on it, will overlap with the PolygonCollider. And can sometimes the object is on top and sometime the polygoncollider , and if the polygoncollider is on top i can not lift the object.
Is there a way to ignore on click all layers except the PickebleObject layer that i use to identify the objects that can be picked up ?
EDIT: here is my ObjectController script
public LayerMask interactLayers;
public LayerMask ignoredColliders;
public Action<GameObject> OnDrag;
public Action<GameObject> OnLand;
private Rigidbody2D objRB2D;
private Vector2 MousePos;
private bool IsOnFloor = false;
private void Start()
{
transform.position = new Vector3 (transform.position.x, transform.position.y, transform.position.z - 0.1f);
ignoredColliders = ~ignoredColliders;
objRB2D = transform.GetComponent<Rigidbody2D>();
objRB2D.gravityScale = GameManager.Instance.GlobalFallingSpeed;
}
private void OnMouseDown()
{
objRB2D.isKinematic = true;
}
private void OnMouseDrag()
{
MousePos = Camera.main.ScreenToWorldPoint (new Vector2 (Input.mousePosition.x, Input.mousePosition.y ));
//Limit So elements cant be moved out of the scene.
float HorizontalClamp = Mathf.Clamp (MousePos.x, CameraControll.Instance.CamTopLeft.x, CameraControll.Instance.CamBottomRight.x);
transform.position = new Vector3 (HorizontalClamp, MousePos.y, transform.position.z);
//Check what is under the mouse.
RaycastHit2D IsHoveringOver = Physics2D.Raycast(transform.position, transform.TransformDirection (Vector3.down), 0 , ignoredColliders);
if (IsHoveringOver.transform != null) {
if (GameManager.Instance.GroundColliders.value == LayerMask.GetMask (LayerMask.LayerToName (IsHoveringOver.transform.gameObject.layer)))
IsOnFloor = true;
}else IsOnFloor = false;
RaycastHit2D HitColider = Physics2D.Raycast(transform.position, transform.TransformDirection (Vector3.down), Mathf.Infinity , ~ignoredColliders);
#if UNITY_EDITOR
if (HitColider.collider != null) {
Debug.DrawLine( HitColider.point, transform.position, Color.magenta);
}
#endif
}
void OnMouseUp()
{
if (!IsOnFloor) objRB2D.isKinematic = false;
}
void OnCollisionEnter2D(Collision2D coll) {
//Debug.Log(coll.gameObject.transform.name);
}
I fixed the problem by using the Z axes to define wich of the colliders to be on top.
If you are using physics raycast in your drag-n-drop logic take a look at this documentation, there you will find a topic about LayerMask and Raycasting.

Categories