Kamera- podstawowe właściwości
Do materiału z poprzedniego tematu (przejdź do poprzedniego tematu) dodamy obsługę kamery. Zadaniem kamery będzie podążanie za ruchem wybranego obiektu (kapsuły lub kuli). Skrypt obsługi kamery może być podpięty bezpośrednio do kamery lub do utworzonego prostego silnika naszej sceny. W tym temacie wybieramy drugie rozwiązanie.
Tworzymy skrypt obsługi kamery
Dodajemy nowy skrypt o nazwie Kamera.cs i podpinamy go do obiektu Silnik
Skrypt Kamera.cs wypełniamy poniższym kodem
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Kamera : MonoBehaviour
{
[SerializeField] private Camera kamera;
private GameObject obserwowanyObiekt=null;
public GameObject ObserwowanyObiekt
{
set { obserwowanyObiekt=value; }
}
private void Obserwuj()
{
if (obserwowanyObiekt == null) return;
kamera.transform.position = obserwowanyObiekt.transform.position;
Debug.Log(obserwowanyObiekt.name);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Obserwuj();
}
}
Stosując metodę set prześlemy wybór aktywnego obiektu za którym ma podążać kamera. Przechodzimy do skryptu Ruch.cs i wprowadzamy zmiany w funkcji aktywnyObiekt(). Patrz poniższy kod
Wskazówka:
void aktywnyObiekt()
{
if (Input.GetKeyUp(KeyCode.Alpha1))
{
aktywny = kapsula;
GetComponent<Kamera>().ObserwowanyObiekt= aktywny;
return;
}
if (Input.GetKeyUp(KeyCode.Alpha2))
{
aktywny = kula;
GetComponent<Kamera>().ObserwowanyObiekt = aktywny;
return;
}
}
Oraz zmiana w funkcji Start()
Wskazówka:
// Start is called before the first frame update
void Start()
{
aktywny = kapsula;
GetComponent<Kamera>().ObserwowanyObiekt = aktywny;
}
To podejście zaoszczędzi nam instrukcji przechwytywania wyboru aktywnego obiektu.
Postać funkcji śledzącej obiekt jest na tę chwile bardzo prosta. Kamera przejmuje współrzędne śledzonego obiektu.
Wskazówka:
private void Obserwuj()
{
if (obserwowanyObiekt == null) return;
kamera.transform.position = obserwowanyObiekt.transform.position;
Debug.Log(obserwowanyObiekt.name);
}
Podpinamy główną kamerę sceny
Skompiluj program i sprawdź efekt działania. Widok nie jest zbyt atrakcyjny ale zaobserwujemy ruch kamery. Poniższa ilustracja pokazuje współrzędne zamocowania kamery (patrz kapsuła) oraz podgląd widoku z kamery (okno Main Camera)
Bardziej atrakcyjnym widokiem jest oddalenie kamery od śledzonego obiektu
Wektor odległości kamery
Kamerę oddalimy od obserwowanego obiektu o pewien wektor z przestrzeni trójwymiarowej. W kodzie skryptu Kamera.cs dodajemy zmienną typu Vector3 o nazwie odleglosc
Wskazówka:
public class Kamera : MonoBehaviour
{
[SerializeField] private Camera kamera;
[SerializeField] private Vector3 odleglosc;
Zmieniamy funkcję obserwowania obiektu na poniższy kod
Wskazówka:
private void Obserwuj()
{
if (obserwowanyObiekt == null) return;
kamera.transform.position = obserwowanyObiekt.transform.position
+ odleglosc;
Debug.Log(obserwowanyObiekt.name);
}
Metodą prób ustalamy parametry współrzędnych wektora odległości. Najłatwiej jest to zrobić kompilując program i wtedy dobrać wartości.
Widok kamery tak ustalony ma pewną wadę. Nie można wycentrować widoku na wskazany obiekt. Możemy to poprawić wykorzystując metodę LookAt klasy Transform. Parametrem tej funkcji współrzędne obserwowanego obiektu. Wprowadzamy kolejna zmianę w kodzie pliku Kamera.cs
Wskazówka:
private void Obserwuj()
{
if (obserwowanyObiekt == null) return;
kamera.transform.position = obserwowanyObiekt.transform.position
+ odleglosc;
kamera.transform.LookAt(obserwowanyObiekt.transform.position);
Debug.Log(obserwowanyObiekt.name);
}
Skompiluj program i sprawdź efekt. Zauważysz, że kamera ogniskuje na wybrany aktywny obiekt.
Widzenie kamery możemy jeszcze poprawić stosując zmianę kąta podniesienia/ skręcenia względem wybranej osi. Do skryptu Kamera.cs dodamy zmienna typu float i przypiszemy jej nazwę katPodniesienia
Wskazówka:
public class Kamera : MonoBehaviour
{
[SerializeField] private Camera kamera;
[SerializeField] private Vector3 odleglosc;
[SerializeField] private float katPodnesienia=0.0f;
Wskazówka:
private void Obserwuj()
{
if (obserwowanyObiekt == null) return;
kamera.transform.position = obserwowanyObiekt.transform.position
+ odleglosc;
kamera.transform.LookAt(obserwowanyObiekt.transform.position);
kamera.transform.Rotate(katPodnesienia, 0, 0);
Debug.Log(obserwowanyObiekt.name);
}
Skompiluj program i przy uruchomionej scenie, zmień i dobierz wartość kąta podniesienia
Wygładzanie ruchu kamery
Ruch kamery możemy wygładzić z lekkim opóźnieniem za śledzonym celem. W tym celu stosuje się funkcje Lerp z klasy Vector3. Zastosowanie metody płynnego najazdu kamery na obiekt będzie wymagało obliczania nowych współrzędnych kamery w funkcji FixedUpdate(), która wykonywana jest z dużo większą częstotliwością niż Update(). W funkcji Update() wykonamy metodę LookAt i Rotate. Modyfikujemy skrypt Kamera.csWskazówka:
void Update()
{
//Obserwuj();
kamera.transform.LookAt(obserwowanyObiekt.transform.position);
kamera.transform.Rotate(katPodnesienia, 0, 0);
}
private void FixedUpdate()
{
kamera.transform.position = Vector3.Lerp(
kamera.transform.position,
obserwowanyObiekt.transform.position + odleglosc,
0.01f);
}
Skompiluj program i sprawdź efekt działania. Zauważysz, że kamera zyskała efekt płynnego najazdu na wybrany obiekt.
Pełny kod skryptu Kamera.cs
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Kamera : MonoBehaviour
{
[SerializeField] private Camera kamera;
[SerializeField] private Vector3 odleglosc;
[SerializeField] private float katPodnesienia=0.0f;
private GameObject obserwowanyObiekt=null;
public GameObject ObserwowanyObiekt
{
set { obserwowanyObiekt=value; }
}
private void Obserwuj()
{
kamera.transform.position = Vector3.Lerp(
kamera.transform.position,
obserwowanyObiekt.transform.position+ odleglosc,
0.01f);
kamera.transform.LookAt(obserwowanyObiekt.transform.position);
kamera.transform.Rotate(katPodnesienia, 0, 0);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//Obserwuj();
kamera.transform.LookAt(obserwowanyObiekt.transform.position);
kamera.transform.Rotate(katPodnesienia, 0, 0);
}
private void FixedUpdate()
{
kamera.transform.position = Vector3.Lerp(
kamera.transform.position,
obserwowanyObiekt.transform.position + odleglosc,
0.01f);
}
}
Pełny kod skryptu Ruch.cs
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ruch : MonoBehaviour
{
[SerializeField] private GameObject kapsula, kula;
//wartość współczynnika predkości
//i momentu siły
[SerializeField] private float v = 2.0f,
moment=500.0f;
private GameObject aktywny;
void aktywnyObiekt()
{
if (Input.GetKeyUp(KeyCode.Alpha1))
{
aktywny = kapsula;
GetComponent<Kamera>().ObserwowanyObiekt= aktywny;
return;
}
if (Input.GetKeyUp(KeyCode.Alpha2))
{
aktywny = kula;
GetComponent<Kamera>().ObserwowanyObiekt = aktywny;
return;
}
}
void ruszaj()
{
float kierunek;
//reaguje na klawisze A,D i strzałki w Lewo, Prawo
if ((kierunek = Input.GetAxis("Horizontal")) != 0)
{
if(aktywny!=kula)
aktywny.transform.Translate(-kierunek*Time.deltaTime*v,0,0);
else
{
Vector3 momentobrotowy=new Vector3(0, 0, kierunek * Time.deltaTime * moment);
aktywny.GetComponent<Rigidbody>().AddTorque(momentobrotowy);
}
}
if ((kierunek = Input.GetAxis("Vertical")) != 0)
{
if (aktywny != kula)
aktywny.transform.Translate(0,0,-kierunek * Time.deltaTime * v);
else
{
Vector3 momentobrotowy = new Vector3(kierunek * Time.deltaTime * moment,0,0);
aktywny.GetComponent<Rigidbody>().AddTorque(momentobrotowy);
}
}
}
// Start is called before the first frame update
void Start()
{
aktywny = kapsula;
GetComponent<Kamera>().ObserwowanyObiekt = aktywny;
}
// Update is called once per frame
void Update()
{
aktywnyObiekt();
ruszaj();
}
}