So I'm new to Unity, but creating a simple 2D game. I need to make two sprites move to each other when clicking on them. I want to use a script attached to the Main Camera but open for other suggestions. Thanks friends!
Here is my script:
public class MoveTo : MonoBehaviour {
GameObject objectA = null;
GameObject objectB = null;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(Input.GetMouseButtonDown(0))
{
Ray rayOrigin = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if(Physics.Raycast(rayOrigin, out hitInfo))
{
//Destroy (hitInfo.collider.gameObject);
if(objectA == null)
{
objectA = hitInfo.collider.gameObject;
}
else{
objectB = hitInfo.collider.gameObject;
}
if(objectA != null && objectB != null)
{
//Need something to make them move towards each other here???
objectA = null;
objectB = null;
}
}
}
}
}
In my opinion moving other gameObjects with a script attached to the camera is breaking the basic idea of components in Unity. It would also be very hard to do if you want the objects to move smoothly and also be able to have multiple pairs of objects moving at same time. You would need some kind of list of moving objects and their destinations. Then you would need to go through the list in the update function of the camera script and update all positions of sprite gameObjects.
I think it is better to attach simple inbetweening script like below to the gameObjects of the sprites.
using UnityEngine;
using System.Collections;
public class Tweener : MonoBehaviour {
public float tweenStopDistance = 0.2f;
public float tweenSpeed = 2.0f;
public Vector3 targetPosition = new Vector3();
void Start () {
targetPosition = transform.position;
}
void Update () {
if((transform.position - targetPosition).magnitude > tweenStopDistance){
transform.position += tweenSpeed * (targetPosition - transform.position).normalized * Time.deltaTime;
}
}
}
In this case you just need to calculate the target position in your camera script.
if(objectA != null && objectB != null)
{
// Calculate position in the middle
Vector3 target = 0.5f * (objectA.transform.position + objectB.transform.position);
// Set middle position as tween target
objectA.GetComponent<Tweener>().targetPosition = target;
objectB.GetComponent<Tweener>().targetPosition = target;
objectA = null;
objectB = null;
}
If you have lot of those sprites and not much computation power on your target machine. You could also attach that script component at run time with gameObject.AddComponent for those sprites that need it. Attaching is probably quite heavy but you wouldn't need to test for already being in the target for sprites that are not moving.
Related
Im working on bomb system in Unity 3D. I have uploaded some explosion effects from Unity Asset Store and I want implement them into my project. I want to plant bomb with key "K" then wait 3sec to detonate it with explosion effect and give some damage to nearby objects. The problem is that explosion appears in such different position as it should. In my opinion this is Editor problem , the code looks fine. I will give you some screenshoots(https://drive.google.com/file/d/19Yzymch9RdTa-E6RkbvvyfzMWjMJHo52/view?usp=sharing)and my bomb script :
public class BoombScript : MonoBehaviour
{
public GameObject boombEffect;
[SerializeField]
private float radius;
[SerializeField]
private float force;
[SerializeField]
private int explosiveDamage;
public void Explode()
{
Instantiate(boombEffect, transform.position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + transform.position);
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, transform.position, radius);
}
}
}
public IEnumerator WaitForExplode()
{
yield return new WaitForSeconds(3f);
Explode();
}
}
In my opinion this is Editor problem , the code looks fine.
In general I would always doubt such a statement.
I can only guess but if I understand you correctly you rather want to use the position of your object the moment when you start the Coroutine, not the one after 3 seconds:
// instead of using the current "reansform.position" rather
// use the pre-stored position passed in via parameter
public void Explode(Vector3 position)
{
Instantiate(boombEffect, position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + position);
Collider[] colliders = Physics.OverlapSphere(position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, position, radius);
}
}
}
// If really needed you could still also have an overload
// e.g. if at some places you actually call this method from the outside
// immediately
public void Explode()
{
Explode(transform.position);
}
public IEnumerator WaitForExplode()
{
// Store the position when the routine is started
var position = transform.position;
yield return new WaitForSeconds(3f);
// Instead of using the current position rather pass in
// the previously stored one of when the routine was started
Explode(position);
}
Before I start I'd like to say sorry if this isn't highly professionally written, I've been working on this for hours banging my head against the wall trying to figure this out with no luck, I'm tired and stressed.
I'm trying to get a moving object to enter through 1 portal at a specific point, and come out the other portal at the same point it entered. So if the ball enters the top of the portal, the object will come out at the top of the exit portal, and the if the object enters from the bottom of the portal it will exit the bottom of the other portal. I'm not the best a illustration, but here's what I want to it do:
Here you can see in both images the object enters the blue portal and exits the orange at the point that it entered, so top to top, bottom to bottom.
I've actually gotten this to work fine, but now I need to do it again but this time, one of the portals needs to be horizontal instead of vertical:
So what I've done, is make it so when both a vertical, I leave a bool called "exitIsHorizontal" unchecked (false), and when one of them is on the ceiling on the level, it translates the vertical axis to a horizontal one.
I even sort of got that to work, however it's got a reproducible quark that needs to be fixed. When the object enters the bottom of the portal, it works fine like you'd expect and just like the image above. But when you hit the top of the portal, the object comes out the other side of the portal like you'd expect, but the object begins moving in the opposite direction as see in this image:
Correct exit location, wrong exit direction for some reason. I also need this function to be dynamic so that if say, the object hit the blue portal from the other direction that the exit direction would switch sides as well like this:
Here is my script:
public GameObject otherPortal;
public PortalController otherPortalScript;
private BallController ballController;
public bool exitIsHorizontal = false;
List<PortalController> inUseControllers = new List<PortalController>();
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Ball")
{
ballController = other.GetComponent<BallController>();
if (inUseControllers.Count == 0)
{
inUseControllers.Add(otherPortalScript);
var offset = other.transform.position - transform.position;
if(exitIsHorizontal)
{
offset.x = offset.y;
offset.y = 0;
}
else
{
offset.x = 0;
}
other.transform.position = otherPortal.transform.position + offset;
}
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.tag == "Ball")
{
inUseControllers.Clear();
}
}
This script is attached to both portals so that both Portals can handle both entering and exiting. And any variable you don't see declared with anything in the script making it essentially null (such as "otherPotal") I declare the in editor.
I'll bet it's something extremely simple that I just keep missing, I just don't know that something is.
So a portal is basically a wormhole. An object that enters will retain its local position and direction.
Functions:
Unity has functions for transforming from world space to local space and vice versa.
Position:
Transform.InverseTransformPoint
Transforms position from world space to local space.
Transform.TransformPoint
Transforms position from local space to world space.
Direction:
For transforming directions you will have to use:
Transform.InverseTransformDirection
Transforms a direction from world space to local space. The opposite
of Transform.TransformDirection.
Transform.TransformDirection
Transforms direction from local space to world space.
Simple Example:
One script that you attach to both portals. It moves objects with the tag "Ball" over to the exitPortal.
public class Portal : MonoBehaviour
{
[SerializeField] Portal exitPortal;
void OnTriggerEnter2D(Collider2D collider)
{
if (collider.CompareTag("Ball"))
{
GameObject ball = collider.gameObject;
Rigidbody2D rigidbody = ball.GetComponent<Rigidbody2D>();
Vector3 inPosition = this.transform.InverseTransformPoint(ball.transform.position);
inPosition.x = -inPosition.x;
Vector3 outPosition = exitPortal.transform.TransformPoint(inPosition);
Vector3 inDirection = this.transform.InverseTransformDirection(rigidbody.velocity);
Vector3 outDirection = exitPortal.transform.TransformDirection(inDirection);
ball.transform.position = outPosition;
rigidbody.velocity = -outDirection;
}
}
}
You get this:
Complex Example:
You need 3 scripts for this to work:
Portal: The thing that touches warpable objects
Warpable: A thing that travels through the portals
Ghost: A mirrored warpable that shows while going through a portal
This is what the ghost looks like:
You will need two additional layers – Portal and Ghost, with the collision matrix set as in the image.
Scripts:
I've added enough comments within the code for you to make sense as to what it's doing.
Portal:
public class Portal : MonoBehaviour
{
[SerializeField] Portal exitPortal;
void OnTriggerEnter2D(Collider2D collider)
{
// When a warpable enters a portal create a ghost
if (collider.TryGetComponent(out Warpable warpable))
{
// Create a ghost only if we haven't already
if (warpable.Ghost == null) warpable.CreateGhost(this, exitPortal);
}
}
void OnTriggerExit2D(Collider2D collider)
{
// When a warpable exist a portal; check if it has a ghost
if (collider.TryGetComponent(out Warpable warpable))
{
// Teleport to the ghost; apply its position, rotation, velocity
if (warpable.Ghost != null)
{
// Create vectors to compare dot product
Vector3 portalToWarpable = warpable.transform.position - this.transform.position;
Vector3 portalDownwards = -this.transform.up;
// If warpable is on the other side of the portal you get a value that's more than zero
float dot = Vector3.Dot(portalDownwards, portalToWarpable);
bool passedThroughPortal = dot >= 0f;
// If we passed through the portal then teleport to the ghost; otherwise just continue
if (passedThroughPortal)
{
warpable.Position = warpable.Ghost.warpable.Position;
warpable.Rotation = warpable.Ghost.warpable.Rotation;
warpable.Velocity = warpable.Ghost.warpable.Velocity;
}
// Destroy the ghost
warpable.DestroyGhost();
}
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.magenta;
Gizmos.DrawRay(this.transform.position, this.transform.up);
}
}
Warpable:
public class Warpable : MonoBehaviour
{
[SerializeField] new Rigidbody2D rigidbody;
public Ghost Ghost { get; private set; }
public void CreateGhost(Portal inPortal, Portal outPortal)
{
// Move the ghost object to the Ghost layer, this is so that ghost can collide with real objects, other ghosts, but not with the portal.
// Ghost/Ghost = TRUE
// Ghost/Default = TRUE
// Ghost/Portal = FALSE
GameObject original = this.gameObject;
GameObject duplicate = GameObject.Instantiate(original);
duplicate.layer = LayerMask.NameToLayer("Ghost");
Physics2D.IgnoreCollision(
original.GetComponent<Collider2D>(),
duplicate.GetComponent<Collider2D>()
);
// Add the ghost component
Ghost = duplicate.AddComponent<Ghost>();
Ghost.observing = original.GetComponent<Warpable>();
Ghost.warpable = duplicate.GetComponent<Warpable>();
Ghost.inPortal = inPortal;
Ghost.outPortal = outPortal;
}
public void DestroyGhost()
{
GameObject.Destroy(Ghost.gameObject);
Ghost = null;
}
public Vector3 Position
{
get { return transform.position; }
set { transform.position = value; }
}
public Quaternion Rotation
{
get { return transform.rotation; }
set { transform.rotation = value; }
}
public Vector3 Velocity
{
get { return rigidbody.velocity; }
set { rigidbody.velocity = value; }
}
}
Ghost:
public class Ghost : MonoBehaviour
{
public Warpable observing;
public Warpable warpable;
public Portal inPortal;
public Portal outPortal;
void FixedUpdate()
{
warpable.Position = OutPosition(observing.Position);
warpable.Rotation = OutRotation(observing.Rotation);
warpable.Velocity = OutDirection(observing.Velocity);
}
Vector3 OutPosition(Vector3 position)
{
Vector3 inPosition = -inPortal.transform.InverseTransformPoint(position);
return outPortal.transform.TransformPoint(inPosition);
}
Quaternion OutRotation(Quaternion rotation)
{
return Quaternion.Inverse(inPortal.transform.rotation) * outPortal.transform.rotation * rotation;
}
Vector3 OutDirection(Vector3 velocity)
{
Vector3 inDirection = -inPortal.transform.InverseTransformDirection(velocity);
return outPortal.transform.TransformDirection(inDirection);
}
void OnDrawGizmos()
{
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(warpable.Position, 1f);
Gizmos.DrawLine(warpable.Position, warpable.Position + warpable.Velocity);
}
}
And the final result is this:
So my endless 2d game has 2 layers of mountains in the background which I want to add parallax on, the Near layer needs to be slower than actor/camera, & the Far to be slowest. The problem is, I can't directly add script of movement to them as they are instantiated in play mode randomly according to the random theme colors, so they are being created one after other from the script below, but I want to add a movement on them on x axis slower than camera speed, while also letting it recreate continuously at the end of last one.
Here's the script creating new mountains :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathManager : MonoBehaviour {
public static PathManager Instance;
public GameObject CoinFragments;
public GameObject SlideArrow;
public float parallaxSpeed = 5f; //
private GameObject lastPathObject;
private float PathXPosition;
private GameObject lastMountainFar;
private float MountainFarXPosition;
private GameObject lastMountainNear;
private float MountainNearXPosition;
private float StarXPostion;
private float lastCameraX; //
// Use this for initialization
void Start () {
Instance = this;
}
// Update is called once per frame
void Update()
{
}
private void LateUpdate()
{
// Check for new Near Mountain
if (lastMountainNear != null && GamePlayCameraManager.Instance.MainCamera.transform.position.x > lastMountainNear.transform.position.x)
{
this.GenerateNearMountain();
}
// Check for new Far Mountain
if (lastMountainFar != null && GamePlayCameraManager.Instance.MainCamera.transform.position.x > lastMountainFar.transform.position.x)
{
this.GenerateFarMountain();
}
}
// Start Creating Mountains
public void StartMountainCreation(){
MountainNearXPosition = GamePlayCameraManager.Instance.MainCamera.transform.position.x;
MountainFarXPosition = GamePlayCameraManager.Instance.MainCamera.transform.position.x;
this.GenerateNearMountain();
this.GenerateFarMountain();
}
private void GenerateNearMountain(){
Vector3 MountainPosition = new Vector3(MountainNearXPosition - 4f, -3.6f, 10f);
lastMountainNear = Instantiate(ThemeManager.Instance.SelectedMountainNear, MountainPosition, Quaternion.identity);
MountainNearXPosition = MountainNearXPosition + lastMountainNear.GetComponent<Renderer>().bounds.size.x - 0.01f;
//float deltaX = GamePlayCameraManager.Instance.MainCamera.transform.position.x - lastCameraX;
//lastCameraX = GamePlayCameraManager.Instance.MainCamera.transform.position.x;
//lastMountainNear.transform.position += Vector3.right * (deltaX * parallaxSpeed);
}
private void GenerateFarMountain(){
Vector3 MountainPosition = new Vector3(MountainFarXPosition - 4f, -3.6f, 22f);
lastMountainFar = Instantiate(ThemeManager.Instance.SelectedMountainFar, MountainPosition, Quaternion.identity);
MountainFarXPosition = MountainFarXPosition + lastMountainFar.GetComponent<Renderer>().bounds.size.x - 0.01f;
}
Here's my camera movement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GamePlayCameraManager : MonoBehaviour {
public static GamePlayCameraManager Instance; // Singleton Instance
public Camera MainCamera; // Main Camera
private Vector3 offset;
// Use this for initialization
void Start () {
Instance = this;
MainCamera.transform.position = new Vector3(0, 0, -10);
}
// Update is called once per frame
void Update () {
}
void LateUpdate()
{
if (GameStateManager.GameState == GameState.Set || GameStateManager.GameState == GameState.Playing)
MainCamera.transform.position = new Vector3(this.offset.x + ActorManager.Instance.Actor.transform.position.x + 0.8f, 0, -10);
}
// Fixed Update Method
void FixedUpdate(){
}
public void FindCameraOffset(){
this.offset = ActorManager.Instance.Actor.transform.position - MainCamera.transform.position + new Vector3(1.5f, 0f, 0f);
}
}
First, you should look into pooling your mountain objects so you don't waste cpu and memory endlessly creating them.
Once you have them pooled in your PathManager, you'll need to first know how far the camera moved in the last frame, and then tell the mountains to move according to that. You are going to have to have a reference to your PathManager in your camera Manager.
In your Camera Manager's LateUpdate:
if (GameStateManager.GameState == GameState.Set || GameStateManager.GameState == GameState.Playing) {
Vector3 newPosition = new Vector3(this.offset.x + ActorManager.Instance.Actor.transform.position.x + 0.8f, 0, -10);
float cameraMoveAmount = newPosition.x - MainCamera.transform.position.x;
MainCamera.transform.position = newPosition;
pathManager.MoveMountains(cameraMoveAmount)
}
Then, in your PathManager, use that moveAmount to change the positions of all of the mountains, as well as where the next one will be created. After this point is where you should check for any need to show new mountains, not in the PathManager's LateUpdate. This is because you need to move the mountains before you check to add new ones, and there is no guarantee which happens first if you have them in different LateUpdate calls.
Something like this:
// How fast the mountain layers are going to move in the direction of the camera.
// 0.0f - will not move with the camera, mountains will slide out of frame at normal speed.
// 1.0f - will move with the camera, no movement will be visible in frame.
public float nearMountainMoveSpeed = 0.5;
public float farMountainMoveSpeed = 0.9f;
. . .
public void MoveMountains(float cameraMoveAmount) {
float nearMountainParallaxMove = nearMountainMoveSpeed * cameraMoveAmount;
float farMountainParallaxMove = farMountainMoveSpeed * cameraMoveAmount;
// Move the near mountains & their next placement
MountainNearXPosition += nearMountainParallaxMove;
foreach (GameObject nearMountain in pooledNearMountains) {
nearMountain.transform.position = nearMountain.transform.position + new Vector3(nearMountainParallaxMove,0f,0f);
}
// Check for new Near Mountain
if (lastMountainNear != null && GamePlayCameraManager.Instance.MainCamera.transform.position.x > lastMountainNear.transform.position.x) {
this.GenerateNearMountain();
}
// Move the far mountains & their next placement
MountainFarXPosition += farMountainParallaxMove;
foreach (GameObject farMountain in pooledFarMountains) {
farMountain.transform.position = farMountain.transform.position + new Vector3(farMountainParallaxMove,0f,0f);
}
// Check for new Far Mountain
if (lastMountainFar != null && GamePlayCameraManager.Instance.MainCamera.transform.position.x > lastMountainFar.transform.position.x) {
this.GenerateFarMountain();
}
}
And as an aside, If you end up changing your code and having a single gameobject with all of the farMountains as children and one with all of the nearMountains as children, then you can just move the transform of those parent gameobjects instead of looping through each individual mountain. The amount you would need to move it is the same, of course.
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;
}
}
}
what i't trying to achieve is have my turrent rotate and follow an "Enemy".
At the moment it detects the enemy but it is not rotating and I don't know why.
The bullet it a prefab i drag in, there has to be a better way to do this? Anyone have any suggestions please?
At the moment the bullet never triggers, but the log Shoot and does...and the rotate doesn't work.
Here is what i have
using UnityEngine;
using System.Collections;
public class TurretController : MonoBehaviour {
public Rigidbody bulletPrefab;
private Transform target;
private GameObject bullet;
private float nextFire;
private Quaternion targetPos;
void OnTriggerEnter(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("in");
target = otherCollider.transform;
StartCoroutine ("Fire");
}
}
void OnTriggerExit(Collider otherCollider) {
if (otherCollider.CompareTag("Enemy"))
{
Debug.Log ("out");
target = null;
StopCoroutine("Fire"); // aborts the currently running Fire() coroutine
}
}
IEnumerator Fire()
{
while (target != null)
{
nextFire = Time.time + 0.5f;
while (Time.time < nextFire)
{
// smooth the moving of the turret
targetPos = Quaternion.LookRotation (target.position);
transform.rotation = Quaternion.Slerp(transform.rotation, targetPos, Time.deltaTime * 5);
yield return new WaitForEndOfFrame();
}
// fire!
Debug.Log ("shoot");
bullet = Instantiate(bulletPrefab, transform.position, transform.rotation) as GameObject;
//bullet.rigidbody.velocity = transform.forward * bulletSpeed;
}
}
}
I tried to change the instantiate part by using this instead
bullet = (GameObject)Instantiate(bulletPrefab, transform.position, transform.rotation);
bullet.GetComponent<Bullet>().target = target.transform;
But then i just get errors like "InvalidCastException: Cannot cast from source type to destination type.
TurretController+c__Iterator0.MoveNext () (at Assets/Scripts/TurretController.cs:44)"
BTW, here's the turret rotation code I used in my project (shared with permission):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Turret : MonoBehaviour
{
[SerializeField]
private float turnRateRadians = 2 * Mathf.PI;
[SerializeField]
private Transform turretTop; // the gun part that rotates
[SerializeField]
private Transform bulletSpawnPoint;
private Enemy target;
void Update()
{
TargetEnemy();
}
void TargetEnemy()
{
if (target == null || target.health <= 0)
target = Enemy.GetClosestEnemy(turretTop, filter: enemy => enemy.health > 0);
if (target != null)
{
Vector3 targetDir = target.transform.position - transform.position;
// Rotating in 2D Plane...
targetDir.y = 0.0f;
targetDir = targetDir.normalized;
Vector3 currentDir = turretTop.forward;
currentDir = Vector3.RotateTowards(currentDir, targetDir, turnRateRadians*Time.deltaTime, 1.0f);
Quaternion qDir = new Quaternion();
qDir.SetLookRotation(currentDir, Vector3.up);
turretTop.rotation = qDir;
}
}
}
class Enemy : MonoBehaviour
{
public float health = 0;
private static HashSet<Enemy> allEnemies = new HashSet<Enemy>();
void Awake()
{
allEnemies.Add(this);
}
void OnDestroy()
{
allEnemies.Remove(this);
}
/// <summary>
/// Get the closest enemy to some transform, optionally filtering
/// (for example, enemies that aren't dead, or enemies of a certain type).
/// </summary>
public static Enemy GetClosestEnemy(Transform referenceTransform, System.Predicate<Enemy> filter=null)
{
// Left as an exercise for the reader.
// Remember not to use Vector3.Distance in a loop if you don't need it. ;-)
// return allEnemies[0];
}
}
First problem: the bullet prefab. The variable type is RigidBody. If you want to treat it as a game object, the variable must be a game object. Or you can instantiate it, cast to RigidBody, then use the .gameObject accessor. Like this:
((RigidBody)Instantiate(theRigidbody)).gameObject
Second problem: start simple with the rotation. If it's not working, don't get fancy yet. Start with something like this (an instant rotation toward the target):
Vector3 targetDirection = target.transform.position - transform.position;
targetDirection.y = 0; // optional: don't look up
transform.forward = targetDirection;
If it works, then add small pieces of additional complexity until it does exactly what you want. And if you don't get things figured out, give me a shout (a comment) on Monday. I've written turret-aiming code (including a maximum rotation speed), and I don't think my boss would mind if I upload it.