Trafienie w tarczę strzelniczą
Ten temat porusza jeden z możliwych sposobów ustalenia punktów za trafienie ustalonych na podstawie przechwycenia współrzędnych kolizji pocisku z celem. Krótki film ilustruje uzyskany efekt
Przygotowujemy tarczę
Tarczę wykonamy w Blenderze z przypisaną teksturą
Eksportujemy do pliku *.fbx i importujemy w Unity. Z zaimportowanej tarczy robimy prefabrykat. Dodajemy Mesh Collider. Zaznaczamy opcję Convex i Is Trigger. Patrz poniższy rysunek.
Przez zmianę położenia i ułożenia kamery ustawiamy większy widok na scenę gry (tak aby można było celować w ruchomą tarczę).
Przechodzimy do prefabrykatu Kula i przypisujemy Tag o nazwie: kula
Skrypt trafienia
Tworzymy skrypt o nazwie TrafienieWTarcze. W edytorze Unity otwieramy edycję prefabrykatu tarczy strzelniczej. Do obiektu Tarcza podpinamy utworzony skrypt. Dodajemy Mesh Collider?a. A w nim ustawiamy opcje Convex i Is Trigger na wybrane. Patrz poniższy rysunek.
Nadpisujemy metodę OnTriggerEnter(). Do testów wprowadzamy informację wykrycia współrzędnych kolizji trafienia kuli w tarczę. Bieżąca postać skryptu poniżej.
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TrafienieWTarcze : MonoBehaviour
{
void OnTriggerEnter(Collider o)
{
if (o.tag == "kula")
{
//pokaż w debugerze współzredne uderzenia pocisku
Debug.Log(o.ClosestPoint(transform.position).ToString());
}
}
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Dla ułatwienia testów blokujemy ruch tarczy przez wyłącznie skryptu ruchu. Patrz poniżej.
Uruchamiamy scenę. Do tarczy podjeżdżamy działem i strzelamy. W komunikatorze możemy odczytać współrzędne trafienia kuli w cel. Co oznacza, że wykrywanie kolizji działa.
W zależności od rozmiarów naszej tarczy musimy ustalić przedziały granicy odległości od jej środka związane z ilością punktów za trafienie. Ustalenie przedziałów ułatwi użycie pustego obiektu Create Empty przesuwanego myszką w siatce tarczy. Znaczenie mają współrzędne Y, Z. Na ich podstawie obliczymy moduł wektora odległości trafienia od środka tarczy.
W zależności jak jest wyjustowana tekstura tarczy, odczyty w waszych projektów mogą być różne w stosunku do podanych przeze mnie. Kolejne odczyty granic zapisujemy w tablicy wektorów 3D.
Wskazówka:
public class TrafienieWTarcze : MonoBehaviour
{
private Vector3[] tab = new Vector3[] {
new Vector3(0.32f,0,0),
new Vector3(0.32f,0.086f,0.024f),
new Vector3(0.32f,0.172f,0.049f),
new Vector3(0.32f,0.358f,0.101f),
new Vector3(0.32f,0.448f,0.126f),
new Vector3(0.32f,0.539f,0.151f),
new Vector3(0.32f,0.628f,0.176f),
new Vector3(0.32f,0.718f,0.201f),
new Vector3(0.32f,0.809f,0.226f),
new Vector3(0.32f,0.897f,0.251f)
};
Podane współrzędne granic przeliczamy na promienie okręgów, czyli długości wektorów 3D. Przy przeliczeniu musimy uwzględnić skalę osadzonego obiektu tarczy w świecie 3D. Do skryptu dodamy tablicę na granice. Przeliczenie zrobimy w funkcji Start(). Patrz poniższy kod.
Wskazówka:
private float[] granica = new float[10];
void Start()
{
//rób granice punktowe obszarów trafień
for(int i = 0; i < granica.Length; i++)
{
granica[i] = new Vector3(0,
tab[i].y * transform.localScale.y,
tab[i].z * transform.localScale.z).magnitude;
Debug.Log("Granica:" + granica[i].ToString());
}
}
Modyfikujemy wyzwalacza kolizji
W metodzie wyzwalacza kolizji z przechwyconych współrzędnych trafienia obliczamy odległość od środka tarczy. Porównujemy z tablicą promieni granic i przydzielamy punkty za trafienie. Zmodyfikowany kod wygląda jak poniżej.
Wskazówka:
void OnTriggerEnter(Collider o)
{
if (o.tag == "kula")
{
//pokaż w debugerze współzredne uderzenia pocisku
Debug.Log(o.ClosestPoint(transform.position).ToString());
Vector3 odleglosc = o.ClosestPoint(transform.position)-transform.position;
float odl= new Vector3(0,
odleglosc.y,
odleglosc.z).magnitude;
//pokaz punkty z indeksu okręgu trafienia
//0 to 10,1 to 9, itd...
int pkt = 0;
for(int i = 0;i < granica.Length; i++)
if (odl > granica[i]) pkt = granica.Length-i;
Debug.Log("Punkty trafienia:"+pkt.ToString());
}
}
Uruchamiamy scenę i sprawdzamy komunikaty w debugerze. Poniżej ilustracja działającej sceny.
Robimy prefabrykaty punktów trafień
W zależności od pomysłowości wizualizację naliczania punktów za trafienie można zrobić w dowolny sposób. Ten pomysł bazuje na siatkach cyfr. Modele przestrzenne cyfr przygotujemy w Blenderze.
Przechodzimy do Unity i importujemy siatki cyfr.
Z zaimportowanych siatek robimy prefabrykaty dla każdej cyfry i jednej liczby (10). Ewentualnie prefabrykat liczby dziesięć może być utworzony z dwóch cyfr 1 i 0. Inicjujemy skrypt ruchu pionowego cyfr .Nadajemy mu nazwę RuchPionowyGora. Skrypt jest prosty. Zawiera dwie zmienne dla współczynników prędkości liniowej i prędkości obrotowej.
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RuchPionowyGora : MonoBehaviour
{
[SerializeField] private float v;
[SerializeField] private float vObrotu;
// Update is called once per frame
void Update()
{
//przesuwaj pionowo w górę
transform.position += Vector3.up * v * Time.deltaTime;
//obarcaj wokól pionowej osi
//tu nalezy odbrać oś doświadczalnie ze wzgeldu na układ
//orientacji wektorów dla siatki
//dobierz gdy na scenie bedzie pojawiac się prefabrykat
transform.Rotate(0,0,vObrotu * Time.deltaTime);
}
}
Skrypt podpinamy do każdego prefabrykatu cyfry. Dobieramy wartości współczynników prędkości według własnego uznania.
Wywołujemy prefabrykaty cyfr w momencie trafienia
Modyfikujemy skrypt TrafienieWTarcze (wcześniej już napisany i podpięty do tarczy) tak aby podczas trafienia został zniszczony pocisk i został utworzony obiekt prefabrykatu cyfry w zależności od zdobytych punktów. Do skryptu dodajemy tablicę na prefabrykaty liczb. Postać skryptu po modyfikacji
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TrafienieWTarcze : MonoBehaviour
{
[SerializeField]GameObject[] prefabLiczba=new GameObject[10];
private Vector3[] tab = new Vector3[] {
new Vector3(0.32f,0,0),
new Vector3(0.32f,0.086f,0.024f),
new Vector3(0.32f,0.172f,0.049f),
new Vector3(0.32f,0.358f,0.101f),
new Vector3(0.32f,0.448f,0.126f),
new Vector3(0.32f,0.539f,0.151f),
new Vector3(0.32f,0.628f,0.176f),
new Vector3(0.32f,0.718f,0.201f),
new Vector3(0.32f,0.809f,0.226f),
new Vector3(0.32f,0.897f,0.251f)
};
private float[] granica = new float[10];
void Start()
{
//rób granice punktowe obszarów trafień
for(int i = 0; i < granica.Length; i++)
{
granica[i] = new Vector3(0,
tab[i].y * transform.localScale.y,
tab[i].z * transform.localScale.z).magnitude;
Debug.Log("Granica:" + granica[i].ToString());
}
}
void OnTriggerEnter(Collider o)
{
if (o.tag == "kula")
{
//pokaż w debugerze współzredne uderzenia pocisku
Debug.Log(o.ClosestPoint(transform.position).ToString());
Vector3 odleglosc = o.ClosestPoint(transform.position)-transform.position;
float odl= new Vector3(0,
odleglosc.y,
odleglosc.z).magnitude;
//pokaz punkty z indeksu okręgu trafienia
//0 to 10,1 to 9, itd...
int pkt = 0;
for(int i = 0;i < granica.Length; i++)
if (odl > granica[i]) pkt = granica.Length-i;
//rob prefabrykat liczby
GameObject p = Instantiate(prefabLiczba[pkt-1],
o.ClosestPoint(transform.position),
Quaternion.Euler(-90, 0, 0));
//Ustaw czas zycia liczby na 2.5 sekundy
Destroy(p, 2.5f);
//zniszcz kule
Destroy(o.gameObject);
}
}
}
Prefabrykaty liczb podłączamy do skrypt. Patrz poniższy rysunek.
Uruchamiamy scenę i sprawdzamy efekt działania. Patrz poniższy rysunek.
Przy trafieniu w tarczę pocisk jest niszczony. Pojawia się eksplozja i liczba punktów trafienia.