Switching between two or more cameras unity3D - c#

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;
}
}
}

Related

How to program a bunch of 1st person animations at different speeds

I have made 4 different types of animation clips in an animator for an an empty gameobject called "Animator". The main camera is a child of this.
The animations feature a running cycle, a walking cycle, a crouch cycle, and an idle cycle. They all have a trigger that let's them play.
How am I able to measure the speed of the player and execute these animations when the player reaches a certain speed. I have found someone else trying to do this and it works but only for idle and walk. But unfortunately I can't get the sprint and crouch to work. I'm not sure what to do, either have the sprint and crouch animations or just change the speed of the walk animation depending on whether the player is sprinting or crouching. I'll leave a comment where the code I found is.
Here's what I have in my player controller (thge trigger stop is for the idle animation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
Animator _ar;
public float speed;
[Range(-5, -20)]
public float gravity = -9.81f;
public float sprintSpeed = 6f;
public float walkSpeed = 4f;
public float crouchSpeed = 2f;
public float standHeight = 1.6f;
public float crouchHeight = 1f;
Vector3 velocity;
bool isGrounded;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public Light _l;
//Set this to the transform you want to check
public Transform objectTransfom;
private float noMovementThreshold = 0.0001f;
private const int noMovementFrames = 1;
Vector3[] previousLocations = new Vector3[noMovementFrames];
public bool isMoving;
//Let other scripts see if the object is moving
public bool IsMoving
{
get { return isMoving; }
}
void Awake()
{
//For good measure, set the previous locations
for (int i = 0; i < previousLocations.Length; i++)
{
previousLocations[i] = Vector3.zero;
}
}
void Start()
{
_ar = GameObject.Find("Animator").GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
//Below here is the code I found. The if statements for isMoving, is what I put in to see if
//this worked.
//Store the newest vector at the end of the list of vectors
for (int i = 0; i < previousLocations.Length - 1; i++)
{
previousLocations[i] = previousLocations[i + 1];
}
previousLocations[previousLocations.Length - 1] = objectTransfom.position;
//Check the distances between the points in your previous locations
//If for the past several updates, there are no movements smaller than the threshold,
//you can most likely assume that the object is not moving
for (int i = 0; i < previousLocations.Length - 1; i++)
{
if (Vector3.Distance(previousLocations[i], previousLocations[i + 1]) >= noMovementThreshold)
{
//The minimum movement has been detected between frames
isMoving = true;
break;
}
else
{
isMoving = false;
}
}
if(isMoving == true)
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = sprintSpeed;
_ar.SetTrigger("WalkSprint");
}
else if (Input.GetKeyUp(KeyCode.LeftControl))
{
speed = crouchSpeed;
_ar.SetTrigger("WalkCrouch");
//transform.localScale = new Vector3(0.8f, 0.5f, 0.8f);
}
else
{
speed = walkSpeed;
_ar.SetTrigger("Walk");
//transform.localScale = new Vector3(0.8f, 0.85f, 0.8f);
}
}
else
{
_ar.SetTrigger("Stop");
}
}
}
Unfortunately, as with many issue in Game Dev, this could be a number of different issues (or all of them!). Here is where you can start to debug:
Check your error log to see if there is anything obvious that jumps out at you, like a bad reference to a Game Object/Componenet.
Watch the animator. You should be able to have the Animator open, side-by-side with your game window while the game is running. You should be able to see the animations running and transitioning. See if something is not linked properly, or if an animation time is configured incorrectly, etc.
Add some debug statements, like outputting the current trigger being set. I would even verify that your inputs are configured correctly. Maybe add some additional conditionals at the beginning that debug what inputs are being pressed.
As #OnionFan said, your Input check is for KeyUp for the Crouch key. That should probably be KeyDown.

(Unity C#) NPC not moving on Grass and other terrain

I have created a NPC that follows the main player. When the player is in a certain range of the NPC, the NPC is supposed to walk, run, and attack based on the distance between the player and the NPC. The NPC has an Animator, box collider, Nav Mesh Agent, Enemy Animator And Enemy Controller Script attached. The settings are as follows,
My problem is that the NPC does not chase the player if there's some sort of grass or ferns on the terrain.
The NPC is set to run all types of terrain using Nav Mesh Agent, moreover the bake settings are like in the image. A video of the issue can be seen here.
The code of the enemy controller (although I doubt that is the issue) is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public enum EnemyState
{
PATROL,
CHASE,
ATTACK
}
public class EnemyController : MonoBehaviour
{
private EnemyAnimator enemy_Anim;
private NavMeshAgent navAgent;
private EnemyState enemy_State;
public float walk_Speed = 0.5f;
public float run_Speed = 4f;
public float chase_Distance = 7f;
private float current_Chase_Distance;
public float attack_Distance = 1.8f;
public float chase_After_Attack_Distance = 2f;
public float patrol_Radius_Min = 20f, patrol_Radius_Max = 60f;
public float patrol_For_This_Time = 15f;
private float patrol_Timer;
public float wait_Before_Attack = 2f;
private float attack_Timer;
private Transform target;
public GameObject attack_Point;
//private EnemyAudio enemy_Audio;
void Awake()
{
enemy_Anim = GetComponent<EnemyAnimator>();
navAgent = GetComponent<NavMeshAgent>();
target = GameObject.FindWithTag(Tags.PLAYER_TAG).transform;
// enemy_Audio = GetComponentInChildren<EnemyAudio>();
}
// Use this for initialization
void Start()
{
enemy_State = EnemyState.PATROL;
patrol_Timer = patrol_For_This_Time;
// when the enemy first gets to the player
// attack right away
attack_Timer = wait_Before_Attack;
// memorize the value of chase distance
// so that we can put it back
current_Chase_Distance = chase_Distance;
}
// Update is called once per frame
void Update()
{
if (enemy_State == EnemyState.PATROL)
{
Patrol();
}
if (enemy_State == EnemyState.CHASE)
{
Chase();
}
if (enemy_State == EnemyState.ATTACK)
{
Attack();
}
}
void Patrol()
{
// tell nav agent that he can move
navAgent.isStopped = false;
navAgent.speed = walk_Speed;
// add to the patrol timer
patrol_Timer += Time.deltaTime;
if (patrol_Timer > patrol_For_This_Time)
{
SetNewRandomDestination();
patrol_Timer = 0f;
}
if (navAgent.velocity.sqrMagnitude > 0)
{
enemy_Anim.Walk(true);
}
else
{
enemy_Anim.Walk(false);
}
// test the distance between the player and the enemy
if (Vector3.Distance(transform.position, target.position) <= chase_Distance)
{
enemy_Anim.Walk(false);
enemy_State = EnemyState.CHASE;
// play spotted audio
// enemy_Audio.Play_ScreamSound();
}
} // patrol
void Chase()
{
// enable the agent to move again
navAgent.isStopped = false;
navAgent.speed = run_Speed;
// set the player's position as the destination
// because we are chasing(running towards) the player
navAgent.SetDestination(target.position);
if (navAgent.velocity.sqrMagnitude > 0)
{
enemy_Anim.Run(true);
}
else
{
enemy_Anim.Run(false);
}
// if the distance between enemy and player is less than attack distance
if (Vector3.Distance(transform.position, target.position) <= attack_Distance)
{
// stop the animations
enemy_Anim.Run(false);
enemy_Anim.Walk(false);
enemy_State = EnemyState.ATTACK;
// reset the chase distance to previous
if (chase_Distance != current_Chase_Distance)
{
chase_Distance = current_Chase_Distance;
}
}
else if (Vector3.Distance(transform.position, target.position) > chase_Distance)
{
// player run away from enemy
// stop running
enemy_Anim.Run(false);
enemy_State = EnemyState.PATROL;
// reset the patrol timer so that the function
// can calculate the new patrol destination right away
patrol_Timer = patrol_For_This_Time;
// reset the chase distance to previous
if (chase_Distance != current_Chase_Distance)
{
chase_Distance = current_Chase_Distance;
}
} // else
} // chase
void Attack()
{
navAgent.velocity = Vector3.zero;
navAgent.isStopped = true;
attack_Timer += Time.deltaTime;
if (attack_Timer > wait_Before_Attack)
{
enemy_Anim.Attack();
attack_Timer = 0f;
// play attack sound
// enemy_Audio.Play_AttackSound();
}
if (Vector3.Distance(transform.position, target.position) > attack_Distance + chase_After_Attack_Distance)
{
enemy_State = EnemyState.CHASE;
}
} // attack
void SetNewRandomDestination()
{
float rand_Radius = Random.Range(patrol_Radius_Min, patrol_Radius_Max);
Vector3 randDir = Random.insideUnitSphere * rand_Radius;
randDir += transform.position;
NavMeshHit navHit;
NavMesh.SamplePosition(randDir, out navHit, rand_Radius, -1);
navAgent.SetDestination(navHit.position);
}
void Turn_On_AttackPoint()
{
attack_Point.SetActive(true);
}
void Turn_Off_AttackPoint()
{
if (attack_Point.activeInHierarchy)
{
attack_Point.SetActive(false);
}
}
public EnemyState Enemy_State
{
get; set;
}
} // class
I would appreciate very it if someone could help with such an issue!
EDIT: I forgot to add the grass settings, these are as follows. As you can see there are no colliders.
I am digging some more and apparently the only walkable area is as follows (not entire map), how can I adjust this?
Below is how I did:
Painted the terrain with all kinds of grasses I needed and trees.
Undone all the painted grasses and trees.
Baked the navigation mesh(Navmesh) onto the terrain.
Redone all the painted grasses and trees.
Boom! Work done. :)
Why Do grasses not walkable?
Grasses have been painted like trees hence they are carved during baking process as trees.
Trick:
You have to paint the grasses and all the trees needed, then you have to unpaint all the grasses exceptional for the trees. After that bake the terrain, and undone the process(pressing CTRL + Z several times) until you see the grasses repainted.

Unity: Animation looping while waiting for state transition

I previously posted this question on the gameDev SE but with no luck, therefore I am trying to see if I could find some help here.
I am having some troubles with the transitions in my animator. Specifically, I am trying to set up a some code to handle combo sequences, and to do so I am using coroutines that exploit the state machine given by the animations in the animator. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityEngine.SceneManagement;
/*enum PlayerState is a list of states that can be taken by the player character. They will be used to implement a finite state machine-like
behavior with the actions it can take*/
public enum PlayerState {
walk,
attack,
interact,
dash,
stagger
}
public class Player_Base : MonoBehaviour {
//Basic parameters
Rigidbody2D myRigidBody; //These are to call the components of the Player GameObject
public static Transform playerPos;
public PlayerState currentState;
Animator myAnimator;
public HealthManager myStatus;
private bool isAlive = true;
//Sorting layers parameters
public CapsuleCollider2D myFeet;
public static float playerVertPos; // Need this to sort other objects layers in another script.
//Movement parameters
[SerializeField] float moveSpeed = 3f; // use [serfiel] or public in order to have something that you can modify from Unity UI
public Vector2 moveInput;
private bool isMoving;//Implementing the state machine and the *blend trees*, you need only to define one bool for all animations of a kind (eg walking anims)
//Combat parameters
private int comboCounter = 0;
private float comboTimer = 0;
//dash parameters
[SerializeField] float dashTimeMax = 1f;
[SerializeField] float dashTime = 0;
[SerializeField] float dashPush = 0.001f;
[SerializeField] float dashSpeed = 10f;
// Use this for initialization
void Start()
{
currentState = PlayerState.walk;//Initial default state of the player
myRigidBody = GetComponent<Rigidbody2D>(); /*the getcomp looks for the related component in the <> and uses it in the code*/
myFeet = GetComponent<CapsuleCollider2D>();
myAnimator = GetComponent<Animator>();
myAnimator.SetFloat("MoveX", 0);//If i do not set a default values for these, if player attacks without moving first, all colliders will activate and will hit all around him
myAnimator.SetFloat("MoveY", -1);
myStatus = GameObject.FindObjectOfType<HealthManager>();
}
// Update is called once per frame
void Update()
{
playerVertPos = myFeet.bounds.center.y;
moveInput = Vector2.zero;/*getaxis and getaxisraw register the input of the axes and outputs +1 or -1 according to the axis direction*/
moveInput.x = Input.GetAxisRaw("Horizontal");
moveInput.y = Input.GetAxisRaw("Vertical");
if (!isAlive)
{
return;
}
else {
if (currentState == PlayerState.walk)//It will consider walking only when in that state, this means that if it is attacking for instance,
//it needs to change its state. Good for compartimentalization of the actions (otherwise I could have changed the direction of the attacks)
{
if (moveInput != Vector2.zero)//This if statement is such that if there is no new input to update the movement with, the last (idle) animation
//will remain, so if you go right and stop, the player keeps facing right
{
Move();
myAnimator.SetFloat("MoveX", moveInput.x);
myAnimator.SetFloat("MoveY", moveInput.y);
myAnimator.SetBool("isMoving", true);
}
else {
myAnimator.SetBool("isMoving", false);
}
}
//Attack inputs
if (Input.GetKeyDown(KeyCode.Mouse0) && currentState != PlayerState.attack)//second clause because i do not want to indefinitely attack every frame
{
StartCoroutine(FirstAttack());
}
if (Input.GetKeyDown(KeyCode.Space) && currentState != PlayerState.dash)
{
StartCoroutine(Dashing());
}
DeathCheck();//check if player is still alive
}
}
public void Move()
{
moveInput.Normalize();
myRigidBody.MovePosition(myRigidBody.position + moveInput * moveSpeed * Time.deltaTime);
//If i want to work with the velocity vector: i have to use rb.velocity, not just taking the xplatinput times movespeed
}
public void MoveOnAnimation(int xMove, int yMove, float displacement)
{
moveInput.x = xMove;
moveInput.y = yMove;
moveInput.Normalize();
myRigidBody.MovePosition(myRigidBody.position + moveInput * displacement * Time.deltaTime);
}
private IEnumerator FirstAttack() {
//Start Attack
comboCounter = 1;
myAnimator.SetInteger("comboSequence", comboCounter);
currentState = PlayerState.attack;
yield return new WaitForSeconds(AttackTemplate.SetDuration(0.6f) - comboTimer);//Problem: if i reduce the time below the animation time of the second animation, the second animation won't go untile the end
comboTimer = AttackTemplate.SetComboTimer(0.4f);
//if combo not triggered:
while (comboTimer >= 0)
{
Debug.Log(comboTimer);
comboTimer -= Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Mouse0))
{
Debug.Log("Chained");
StopCoroutine(FirstAttack());
StartCoroutine(SecondAttack());
}
yield return null;
}
comboCounter = 0;
myAnimator.SetInteger("comboSequence", comboCounter);
currentState = PlayerState.walk;
}
private IEnumerator SecondAttack()
{
comboCounter = 2;
myAnimator.SetInteger("comboSequence", comboCounter);
currentState = PlayerState.attack;
yield return null;
//if combo not triggered:
yield return new WaitForSeconds(AttackTemplate.SetDuration(0.9f));
comboCounter = 0;
myAnimator.SetInteger("comboSequence", comboCounter);
currentState = PlayerState.walk;
}
private void Dash()
{
if (dashTime >= dashTimeMax)
{
dashTime = 0;
myRigidBody.velocity = Vector2.zero;
currentState = PlayerState.walk;
}
else
{
currentState = PlayerState.dash;
dashTime += Time.deltaTime;
moveInput.Normalize();
Vector2 lastDirection = moveInput;
myRigidBody.velocity = lastDirection * dashSpeed;
}
}
private IEnumerator Dashing()
{
currentState = PlayerState.dash;
for (float timeLapse = 0; timeLapse < dashTime; timeLapse = timeLapse + Time.fixedDeltaTime)
{
moveInput.Normalize();
Vector2 lastDirection = moveInput;
myRigidBody.velocity = lastDirection * dashSpeed;
}
yield return null;
myRigidBody.velocity = Vector2.zero;
currentState = PlayerState.walk;
}
private void DeathCheck() //if the player health reaches 0 it will run
{
if (HealthManager.health == 0) {
isAlive = false; // this is checked in the update, when false disables player inputs
myRigidBody.constraints = RigidbodyConstraints2D.FreezePosition; // if i don't lock its position, the last bounce with the enemy pushes the player towards inifinity
myAnimator.SetTrigger("death");//triggers the death animation
StartCoroutine(LoadNextScene());
}
}
[SerializeField] float LevelLoadDelay = 5f;
[SerializeField] float LevelSlowMo = 1f;
IEnumerator LoadNextScene()
{
Time.timeScale = LevelSlowMo;
yield return new WaitForSecondsRealtime(LevelLoadDelay);
Time.timeScale = 1f;
var CurrentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(CurrentSceneIndex + 1);
}
}
What I am doing basically is to use enums to define the player states and on input, if the player is not already in the attacking state, perform an attack. Once the FirstAttack() is called, it will first of all update an integer, comboCounter, which handles the transitions between consecutive attacks, input said integer in the animator and then change my state to attack. After this, I created a while loop that goes on until the end of an established time interval during which the player would be able to press the same attack button to chain the combo. If this does not happen, the state and integer parameter are reset.
The problem I am facing is that while the player can actually perform the combo with the second attack, during all the interval in which the first animation is active it keeps looping. Furthermore, I noticed that the second animation does not reach the end, it seems like it stops once the interval that I previously set will end.
Update: This is the screenshot of my animator window:
The transitions any state -> 1stAttack and 1stAttack -> 2ndAttack is handled by the same integer parameter, comboSequence, which is set to 0 normally, to 1 for 1stAttack and to 2 for the second one. I observed that the transition any state -> 1stAttack is triggered multiple times whenever I press the hit button, in line with the looping problem I am facing.
I have tried a couple of things, for instance using normal functions instead of a coroutine, but in this way, I do not understand why, there are problems with the enums states, also I think that in the long term this approach would be more modular and customisable. I feel like I am missing something trivial but I do not understand what and it has been some time now, so any help would be much appreciated!
Disable Can Transition To Self = false

Main maintain 360 rotation while toggling between VR and Normal mode

I have developed a VR game using Unity and Google VR SDK for Android. I want the game to be playable without a VR headset too. How should I implement switching from VR to Normal mode and vice versa? I want to maintain 360 rotation while in Normal Mode using the phone gyroscope. I have looked through many scripts online, but I can't find anything that would make this possible.
I have found that switching modes can be done using XRSettings.enabled = true/false (depending on the mode), but how to maintain 360 rotation while in Normal (Non VR mode)
Here is the script I wrote:
public class GyroToggleManager : MonoBehaviour {
private int flag = 0;
private Quaternion offset;
IEnumerator SwitchToVR() {
string desiredDevice = "cardboard";
XRSettings.LoadDeviceByName(desiredDevice);
yield return null;
XRSettings.enabled = true;
transform.localRotation = Quaternion.identity;
}
IEnumerator SwitchTo2D() {
Input.gyro.enabled = true;
// couldn't figure out how to find this.
offset = ;
XRSettings.LoadDeviceByName("");
yield return null;
transform.localRotation = Quaternion.identity;
}
// Use this for initialization
void Start () {
if(XRSettings.enabled == false){
Input.gyro.enabled = true;
}
}
// Update is called once per frame
void Update () {
if (XRSettings.enabled) {
return;
}
//Also tried different combinations here nothing worked.
transform.localRotation = Input.gyro.attitude ;
}
public void StartVR(){
if(XRSettings.enabled == false){
StartCoroutine (SwitchToVR ());
}
}
public void StartN(){
if(XRSettings.enabled == true){
StartCoroutine(SwitchTo2D());
}
}
}
Updated Script:
public class GyroToggleManager : MonoBehaviour {
Quaternion offset;
IEnumerator SwitchToVR() {
string desiredDevice = "cardboard";
XRSettings.LoadDeviceByName(desiredDevice);
yield return null;
XRSettings.enabled = true;
transform.rotation = Quaternion.identity;
}
IEnumerator SwitchTo2D()
{
Input.gyro.enabled = true;
//Get offset.. Subtract Camera rotation from Gyro rotation
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
XRSettings.LoadDeviceByName("");
yield return null;
XRSettings.enabled = false;
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
// Use this for initialization
void Start () {
if(XRSettings.enabled == false){
Input.gyro.enabled = true;
}
}
void Update()
{
if (XRSettings.enabled)
{
return;
}
//Add the gyro value with the offset then apply to the camera
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
public void StartVR(){
if(XRSettings.enabled == false){
StartCoroutine (SwitchToVR ());
}
}
public void StartN(){
if(XRSettings.enabled == true){
StartCoroutine(SwitchTo2D());
}
}
}
Below is a simple camera follow script that follows a player ball while maintaining the offset distance between the camera and the player. It uses an offset value to do that by subtracting the camera's position from the player's position and then re-applying that offset to the camera's position with current player position in the Update or LateUpdate function.
public Transform playerTransform;
public Transform mainCameraTransform = null;
private Vector3 cameraOffset = Vector3.zero;
void Start()
{
mainCameraTransform = Camera.main.transform;
//Get camera-player Transform Offset that will be used to move the camera
cameraOffset = mainCameraTransform.position - playerTransform.position;
}
void LateUpdate()
{
//Move the camera to the position of the playerTransform with the offset that was saved in the beginning
mainCameraTransform.position = playerTransform.position + cameraOffset;
}
The example and code above is not exactly your solution but it's the easiest way to understand what you need to do.
In your case you need to subtract the camera's rotation from the gyro sensor or Input.gyro.attitude. The minor changes is that you can't really use - or + for that since both are Quaternion not Vector3 as in the example above.
To subtract a Quaternion from another Quaternion like I did in the
Start function with Vector3, multiply the inverse of the other
Quaternion. The inverse is
obtained with Quaternion.Inverse.
To add two Quaternions like I did in the LateUpdate function
above with Vector3, simply multiply both Quaternion together.
Here is the relevant changes in your code:
Quaternion offset;
IEnumerator SwitchTo2D()
{
Input.gyro.enabled = true;
//Get offset.. Subtract Camera rotation from Gyro rotation
offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
XRSettings.LoadDeviceByName("");
yield return null;
}
// Update is called once per frame
void Update()
{
if (XRSettings.enabled)
{
return;
}
//Add the gyro value with the offset then apply to the camera
transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
The GyroToUnity function is used to convert the gyroscope coordinate into Unity's coordinate before applying it to the camera. The gyroscope sensor is using the right-handed coordinate while Unity's camera and other objects are using the left-handed coordinate. See this for more information.

Any way of moving my player other then the controller script teleports

I'm trying to make a spring wall in my 2d platformer. However every way I've tried to move my character (addforce, transform.translate and even tried using the bouncy naterial) teleports my character rather then moving it. This doesn't happen with my controller script. I suspect something in my script is causing this interaction but I'm not sure exactly what. Here is my controller script. Any suggestions would be greatly appreciated :))
using UnityEngine;
using System.Collections;
public class controller : MonoBehaviour
{
//how fast he can go
public float topSpeed = 15f;
bool facingRight = true;
//what direction character is facing
bool grounded = false;
//check if the character is grounded
public Transform groundCheck;
//the transform is used to see if character has touched the ground yet
float groundRadius = 0.2f;
//creates a ground radius for the transform circle for ground detection
GameObject Player, Player2;
int characterselect;
//I'm pretty sure this stuff ^^ is unnessecary and is from when I had the character switch in this script but dont want to remove just in case
public float jumpForce = 700f;
//the characters jumpforce
public GameObject jumpParticles;
public LayerMask whatIsGround;
//what layer is the ground
void Start()
{
characterselect = 1;
Player = GameObject.Find("Player");
Player2 = GameObject.Find("Player2");
//loads game objects as variables I'm pretty sure this stuff ^^^^ is unnessecary and is from when I had the character switch in this script but dont want to remove just in case
}
void FixedUpdate()
{
//has the transform hit the ground yet returns a true or false value
grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
// get direction
float move = Input.GetAxis("Horizontal");
//add velocity to the move direction times by the speed
GetComponent<Rigidbody2D>().velocity = new Vector2(move * topSpeed, GetComponent<Rigidbody2D>().velocity.y);
if (move > 0 && !facingRight) //if facing not right then use the flip function
flip();
else if (move < 0 && facingRight)
flip();
//the whole flip turned out not to be nesseary as my sprites were symetric
}
void Update()
{
//if the character is in fact touching the ground then when space is pressed the function will run
if(grounded&& Input.GetKeyDown(KeyCode.Space))
{
//adds the jump force to the rigidbody attached to the character
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));
//Instantiate(jumpParticles, transform.position, transform.rotation);
//Destroy(jumpParticles, 3f);
}
{
//this was code I was working on for character switching but couldn't get it to work properly. I didn't delete it because it took me ages to do and was recycled in the characterswitch script
// if (Input.GetKeyDown(KeyCode.E))
// {
// if (characterselect==1)
// {
// characterselect = 2;
// }
// else if (characterselect==2)
// {
// characterselect = 1;
// }
// }
// if (characterselect==1)
// {
// Player.SetActive(true);
// Player2.SetActive(false);
// }
// else if (characterselect==2)
// {
// Player.SetActive(false);
// Player2.SetActive(true);
// }
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
topSpeed = topSpeed * 2;
}
else if (Input.GetKeyUp(KeyCode.LeftShift))
{
topSpeed = 15;
}
}
void flip()
{
//for when facing the other direction
facingRight = ! facingRight;
//load the local scale
Vector3 theScale = transform.localScale;
//flip character on the x axis
theScale.x *= -1;
//and then apply it to the local scale
transform.localScale = theScale;
}
Edit
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class boost : MonoBehaviour {
private Rigidbody2D rb2d;
// Use this for initialization
void OnCollisionEnter2D(Collision2D other){
if (other.gameObject.tag == "booster") {
Vector2 tempvect = new Vector2 (2, 0);
rb2d.MovePosition ((Vector2)transform.position + tempvect);
}
}
void Start () {
rb2d = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
}
}
This is the code that I think should make it work the error comes at
rb2d.MovePosition ((Vector2)transform.position + tempvect);

Categories