Jump to content
Unity Insider Forum

3rd Person Camera --> Clipping (Sehr Anspruchsvoll)


Worms123

Recommended Posts

Hallo zusammen!

 

Ich habe folgendes Problem. Doch bevor ich dies genauer erläutern kann möchte ich erklären was bereits funktioniert.

 

Was bereits Funktioniert:

 

Via Scripting konnte ich mir eine third-Person Camera basteln welches sich mit dem Controller steuern lässt. Mit dem linken Joystick kann ich den Spieler bewegen, mit dem Rechten die Kamera in der x,y Achse rotieren (sphärisch um den Spieler).

Mit Transform Translate dreht sich der Spieler ( in meinem Fall eine Kapsel) in die Z Richtung der Kamera (ohne dass sich der Spieler neigt wenn die Kamera in die Y Achse rotiert).

Ich arbeite mit 5 Raycasts. 4 davon verlaufen in den Öffnungswinkel der Kamera (Clippingpoints). Eine trifft die ganze Zeit den Spieler.

Falls eine der 5 Clippingrays auf ein Hindernis trifft, fährt die Kamera näher zum Spieler hin.

 

 

 

Nun zum Problem:

 

Meine Kamera Clippt durch Wände. Ich arbeite bereits mit einem Physics.CheckSphere und die funktioniert relativ gut wenn ich die Kamera in Richtung der Wand drehe. Wenn ich jedoch von einem komischen Winkel zur Wand hinlaufe Clippt meine Kamera dennoch hinein. Wenn ich rückwärts laufe (quasi gegen die Kamera) Clippt sie ebenfalls durch.

 

 

 

Was ich bereits probiert habe:

- Sphere Collider & Rigidbody --> Nützt nichts, da ich die Kamera mittels Joystick in die Wand rein

drücke.

 

- RayCasts ---> bringt nur bei gewissen Winkel etwas ausserdem wird das Script schnell unübersichtilich

 

- Unity Cameras---> Sie bringen mir nicht die gewünschten Ergebnisse. Ausserdem wollte ich von Grund auf die Kamera selber programmieren.

 

 

 

Mein Wunsch:

 

- Die Kamera soll Wänden und Hindernissen automatisch aus den weg gehen.

- Wenn sich der Spieler auf die Kamera zu bewegt und hinter der Kamera befindet sich eine Wand soll die Kamera rotieren.

- Wenn sich die Kamera in eine Ecke befindet soll sie sich einigermassen elegant rausmanövrieren ohne in die Wand gedrückt zu werden.

 

 

Ich bedanke mich im Voraus für jede Hilfe die ich bekommen kann.

Link zu diesem Kommentar
Auf anderen Seiten teilen

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThirdPersonCameraScript : MonoBehaviour {
public GameObject target;
public Camera cam;

private  float Y_Angle_MIN = 1.0f;   // für Y Winkel der Kamera
private  float Y_Angle_Max = 40.0f;
private float X_Angle_MIN;
private float X_Angle_Max;
private float currentX = 0.0f;   // wird gebraucht um Kalkulationen auszuführen
private float currentY = 0.0f;  // wird gebraucht um Kalkulationen auszuführen
private float distance = 10.0f; // Distanz zwischen Camera und spieler
public float MIN_Distance =5; //  Für maximal und minimal distanz der kamera
public float MAX_Distance = 10;
public float cameraspeed = 10; // Geschwindigkeit der Kamera
public float rayLength;  // strahlenlänge

public Transform lookAt;  // Spieler Transform position
private Transform camTransform; // Camera Transform position
public Vector3[] intoArray;  // Array für die Clipping Plane Punkte der kamera

bool ClipPointsFree = true;
public bool hitsTheWall= false;

void Start () {
 camTransform = transform;
 cam = Camera.main; // cam ist gleich der main Camera in der Szene (mit dem Tag main)
 rayLength = MAX_Distance;
}
void Update () {
 currentY = Mathf.Clamp (currentY, Y_Angle_MIN, Y_Angle_Max); // ein Clamp um den Radius zu veringern (siehe rotation im Inspector)
 distance = Mathf.Clamp (distance,MIN_Distance,MAX_Distance); // setzt eine Distanzlimmite

}
private void LateUpdate ()
{
 if (!Camera.main)
  return;
 //currentX += (int)Input.GetAxis ("Mouse X")*5;  // bei Input Manager muss noch der Input von Mouse auf Joystick3thd gewechselt werden
 //currentY += (int)Input.GetAxis ("Mouse Y")*5;
 target.GetComponent<Rigidbody> ().rotation = Quaternion.AngleAxis (currentX, Vector3.up); 
 // Erstellt einen 2dimensionalen Kreis um die spähre und berrechnet die x achse der Kamera.
 // d.h. der Spieler dreht sich um die eigene achse wenn sich die kamera dreht!

 #region KameraEckPunkte
 intoArray = new Vector3[5];
 float z = Camera.main.nearClipPlane;
 float x = Mathf.Tan (Camera.main.fieldOfView / 3.41f) * z;
 float y = x / Camera.main.aspect;
 Quaternion atRotation = new Quaternion ();
 //bottom right
 intoArray [0] = (atRotation * new Vector3 (-x, y, z)) + gameObject.transform.forward;
 //bottom left
 intoArray [1] = (atRotation * new Vector3 (x, y, z)) + gameObject.transform.forward;
 // top right
 intoArray [2] = (atRotation * new Vector3 (-x, -y, z)) + gameObject.transform.forward;
 // top left
 intoArray [3] = (atRotation * new Vector3 (x, -y, z)) + gameObject.transform.forward;
 //cameraPosition
 intoArray [4] = gameObject.transform.position - Camera.main.transform.forward;
 #endregion // Berrechnungen der Kamera Clipping Planes mit Trigonometrie
 #region dieVierRays
 //BottomRight Raycast
 Ray bottomRightray = new Ray (); 
 bottomRightray.origin = gameObject.transform.position;
 bottomRightray.direction = intoArray [0]; //Kameradirection zum endpunkt der kamera hin
 Debug.DrawRay (bottomRightray.origin, bottomRightray.direction * rayLength, Color.red);
 RaycastHit bottomrighthit;
 if (Physics.Raycast (bottomRightray, out bottomrighthit, rayLength)) {  // ray wird hier mitgegeben, out hit bedeutet das sie information in hit gespeichert wird , 10 ist die distanz
  Debug.DrawRay (bottomRightray.origin, bottomRightray.direction * 10, Color.cyan); // hier kann ich meinen ray sehen.
  if (bottomrighthit.collider.tag == "Wall") {
   if (distance > MIN_Distance)
 distance = distance - 0.5f; // falls die Kamera auf eine Wand trifft geht sie nach vorne
   ClipPointsFree = false;
  }
 }

 //bottomLeft
 Ray bottomLeftRay = new Ray (); 
 bottomLeftRay.origin = gameObject.transform.position;
 bottomLeftRay.direction = intoArray [1];
 Debug.DrawRay (bottomLeftRay.origin, bottomLeftRay.direction * rayLength, Color.red);
 RaycastHit bottomlefthit;
 if (Physics.Raycast (bottomLeftRay, out bottomlefthit, rayLength)) {  // ray wird hier mitgegeben, out hit bedeutet das sie information in hit gespeichert wird , 10 ist die distanz
  Debug.DrawRay (bottomLeftRay.origin, bottomLeftRay.direction * 10, Color.cyan); // hier kann ich meinen ray sehen.
  if (bottomlefthit.collider.tag == "Wall") {   
   if (distance > MIN_Distance)
 distance = distance - 0.5f; // falls die Kamera auf eine Wand trifft geht sie nach vorne
   ClipPointsFree = false;
  }
 }
 //topRight
 Ray topRightray = new Ray (); 
 topRightray.origin = gameObject.transform.position;
 topRightray.direction = intoArray [2];
 Debug.DrawRay (topRightray.origin, topRightray.direction * rayLength, Color.red);
 RaycastHit toprighthit;
 if (Physics.Raycast (topRightray, out toprighthit, rayLength)) {  // ray wird hier mitgegeben, out hit bedeutet das sie information in hit gespeichert wird , 10 ist die distanz
  Debug.DrawRay (topRightray.origin, topRightray.direction * rayLength, Color.cyan); // hier kann ich meinen ray sehen.
  if (toprighthit.collider.tag == "Wall") {
   if (distance > MIN_Distance)
 distance = distance - 0.5f; // falls die Kamera auf eine Wand trifft geht sie nach vorne
   ClipPointsFree = false;
  }
 }

 //topLeft
 Ray topLeft = new Ray (); 
 topLeft.origin = gameObject.transform.position;
 topLeft.direction = intoArray [3];
 Debug.DrawRay (topLeft.origin, topLeft.direction * rayLength, Color.red);
 RaycastHit toplefthit;
 if (Physics.Raycast (topLeft, out toplefthit, rayLength)) {  // ray wird hier mitgegeben, out hit bedeutet das sie information in hit gespeichert wird , 10 ist die distanz
  Debug.DrawRay (topLeft.origin, topLeft.direction * rayLength, Color.cyan); // hier kann ich meinen ray sehen.
  if (toplefthit.collider.tag == "Wall") {
   if (distance > MIN_Distance)
 distance = distance - 0.5f; // falls die Kamera auf eine Wand trifft geht sie nach vorne
   ClipPointsFree = false;
  }
 }
 #endregion
 if (!Physics.Raycast (topRightray, out toprighthit, rayLength) && distance <= MAX_Distance &&
  !Physics.Raycast (topLeft, out toplefthit, rayLength) && distance <= MAX_Distance &&
  !Physics.Raycast (bottomRightray, out bottomrighthit, rayLength) && distance <= MAX_Distance &&
  !Physics.Raycast (bottomLeftRay, out bottomlefthit, rayLength) && distance <= MAX_Distance) {
  ClipPointsFree = true;
 }
 if (ClipPointsFree == true) {  // Falls die Rays der Kameras nicht mit der Wand kollidieren soll die Kamera an distanz zunehmen
  if (distance < MAX_Distance) {
   distance += 0.5f;
   hitsTheWall = false;
  }
  if (distance > MAX_Distance)
   distance = MAX_Distance;
 }

 Ray ray = new Ray ();  // eine neues Objekt mit der Klasse Ray wird erstellt
 ray.origin = gameObject.transform.position; // ihm werden position und die richtung mitgegeben
 ray.direction = transform.forward;  // ray direction in der Z achse, da sie sonst irgendwohin strahlt
 RaycastHit hit;
 if (Physics.Raycast (ray, out hit, MAX_Distance)) {  // ray wird hier mitgegeben, out hit bedeutet das sie information in hit gespeichert wird , 10 ist die distanz
  Debug.DrawRay (ray.origin, ray.direction * MAX_Distance, Color.cyan); // hier kann ich meinen ray sehen.
  if (hit.collider.tag == "Wall") {

   if (hit.collider.tag == "Player") {
 //Debug.Log ("I hit the Player");
   }
  }
 }

 if (Physics.CheckSphere (gameObject.transform.position,1)) {  // erstellt eine Sphere um die kamera die kollisionen abfragt
   Debug.Log (" i am casting");
 if (distance > MIN_Distance)
   distance = distance - 0.5f * Time.smoothDeltaTime; // falls die Kamera auf eine Wand trifft geht sie nach vorne
 ClipPointsFree = false;
 Debug.Log ("sphere hits the Wall");
 hitsTheWall = true; 
  }

  if (hitsTheWall == false) {  // falls die Kamera nicht mit einer Wand Kollidiert
   currentX += (int)Input.GetAxis ("Mouse X") *cameraspeed ;  // bei Input Manager muss noch der Input von Mouse auf Joystick3thd gewechselt werden
   currentY += (int)Input.GetAxis ("Mouse Y") * cameraspeed;
   Vector3 dir = new Vector3 (0, 0, -distance);
   Quaternion rotation = Quaternion.Euler (currentY, currentX, 0);
   Debug.Log (" should not rotate");
   camTransform.position = lookAt.position + rotation * dir;
   camTransform.LookAt (lookAt.position);
  }
  if (hitsTheWall == true) { // if spherecast hits the wall

  currentY += (int)Input.GetAxis ("Mouse Y") * cameraspeed;
  //currentX += (int)Input.GetAxis ("Mouse X") * cameraspeed;
   //currentX += (int)Input.GetAxis ("Mouse X") * -Vector3.Distance (gameObject.transform.position, spherehit.point);
   //currentX += 0;
  currentX += (int)Input.GetAxis("Mouse X") * 0.01f; // falls die Kamera mit einer Wand kollidiert bewegt sie sich ganz langsam

   Vector3 dir = new Vector3 (0, 0, -distance);
   Quaternion rotation = Quaternion.Euler (currentY, currentX, 0);
   Debug.Log (" should not rotate");
   camTransform.position = lookAt.position + rotation * dir ;
   camTransform.LookAt (lookAt.position);

  }
 }

}

Was genau meinst du mit "komischen Winkel"? Ich denke mal da müste man eine genauere Erklärung + Code sehen. Außerdem frage ich mich, warum das mit einem einfachen Raycast nicht funktioniert.

Link zu diesem Kommentar
Auf anderen Seiten teilen

- Wenn sich der Spieler auf die Kamera zu bewegt und hinter der Kamera befindet sich eine Wand soll die Kamera rotieren.

- Wenn sich die Kamera in eine Ecke befindet soll sie sich einigermassen elegant rausmanövrieren ohne in die Wand gedrückt zu werden.

Das wird einigermaßen unmöglich.

Wenn du nicht deine ganzen Szenen mit handgesetzten Daten übersähst,die der Kamera sagen, wie sie sich zu bewegen hat, wirst du immer Fälle haben, in denen die Kamera entweder irgendwo reinclipt oder Spasmen kriegt.

 

Die weit verbreitete Lösung ist, einen einzelnen Ray (alternativ Spherecast) vom einem Punkt in der Spielfigur in Richtung der Kamera zu schießen und die Kamera auf die gewünschte Position oder die des RaycastHits zu setzen, je nach dem, was näher dran ist. Ist auch wesentlich weniger verwirrend als eine Kamera, die sich steuern lässt, dann aber plötzlich selbstständig wird und durch die Gegend fliegt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das wird einigermaßen unmöglich.

Wenn du nicht deine ganzen Szenen mit handgesetzten Daten übersähst,die der Kamera sagen, wie sie sich zu bewegen hat, wirst du immer Fälle haben, in denen die Kamera entweder irgendwo reinclipt oder Spasmen kriegt.

 

Die weit verbreitete Lösung ist, einen einzelnen Ray (alternativ Spherecast) vom einem Punkt in der Spielfigur in Richtung der Kamera zu schießen und die Kamera auf die gewünschte Position oder die des RaycastHits zu setzen, je nach dem, was näher dran ist. Ist auch wesentlich weniger verwirrend als eine Kamera, die sich steuern lässt, dann aber plötzlich selbstständig wird und durch die Gegend fliegt.

 

 

Danke viel Mals Sascha das erscheint mir logisch und umzusetzbar :lol:

Und ich bin auch froh zu hören das ich hier das Maximum aus dem hier rausgeholt habe was möglich ist. ^_^

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...