[B3d] Linepick komplett enträtselt

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

Bob

Betreff: [B3d] Linepick komplett enträtselt

BeitragDo, Feb 23, 2006 14:07
Antworten mit Zitat
Benutzer-Profile anzeigen
Blitzbasic Tutorial
Robert "bob" Chlewinski Feb.2006

Linepick komplett enträtselt.
Schwierigkeitsgrad: Ambitioniert Anfänger
Ziel: Die Linepick Funktion richtig anwenden und Fallstricke erkennen und meiden.


Die Linepick Funktion wird meist dazu verwendet um festzustellen ob ein Objekt von einem anderen Objekt aus „gesehen“ werden kann. Ist eines der beiden Objekte eine Camera, versuchen viele Anfänger das Problem zunächst mit der Funktion ENTITYINVIEW zu lösen. In der Hilfe zu diesem Befehl steht:
„Die Funktion liefert 1 zurück, wenn ein Objekt in einer Kamera sichtbar ist, ansonsten wird 0 zurückgeliefert.“ Der Irrtum dem viele hierbei aufsitzen ist schnell erklärt.
Die ENTITYINVIEW Funktion prüft nicht ob sich zwischen Camera und Zielobjekt weitere Objekte befinden und somit das Zielobjekt eventuell verdecken. Sie Prüft also nur auf ein theoretisches sehen bzw. ob das Zielobjekt sich innerhalb des Kameraprojektionsbereich befindet. Damit ist diese Funktion für eine Sichtbarkeitsüberprüfung nach unseren Vorstellungen nicht zu gebrauchen.

Linepick und die Parameter:
In der deutschen Hilfe zu Linepick steht folgendes:


-----------------------------------------------------------------------
Entity=LINEPICK (X#, Y#, Z#, DX#, DY#, DZ# [,Radius#])

Beschreibung Blitz3D

Die Anweisung LINEPICK ermittelt die Identität des ersten Objekts, welches eine Linie vom Startpunkt zum Endpunkt berührt.

Der Startpunkt der Linie befindet sich an Position X,Y,Z

Die Abstände der Linie sind DX,DY und DZ.

Der Radius einer Linie kann auch eingestellt werden (vergleichbar mit Durchmesser eines Rohrs).

Ein Entity muss mit ENTITYPICKMODE auf einen größeren Wert als Null gesetzt werden, damit diese Funktion korrekt arbeitet.


Parameter
X = X-Koordinate des Startpunkts
Y = Y-Koordinate des Startpunkts
Z = Z-Koordinate des Startpunkts
DX = X-Abstand der Linie (Delta)
DY = Y-Abstand der Linie (Delta)
DZ = Z-Abstand der Linie (Delta)
Radius = Radius der Linie (Standard=0)

-----------------------------------------------------------------------



Der Startpunkt X,Y,Z der Funktion dürfte wohl allen klar sein. Dies sind die aktuellen Koordinaten des prüfenden Objekts. Ein erster Fallstrick ergibt sich allerdings schon hier wenn das Prüfende Objekt eine Parent besitzt (z.B. ein Pivot) und dieser nicht an derselben Position ist wie das prüfende Objekt. Später dazu mehr.
Die Abstandsparameter(Delta) stellen die Anfänger oft vor das erste Problem. Was genau ist damit gemeint und wie berechne ich die Delta Werte?
Wir erstellen nun eine erste Test Situation die wir im Verlauf des Tutorials immer weiter verkomplizieren. Sinn und Zweck dabei ist die Fallstricke zu finden und zu beseitigen.


Situation 1: Keep it simple
Nehmen wir folgende, einfache Situation an.
Eine Kamera, ohne Parent, befindet sich an Position 1,1,1 im 3D Raum. Ein Cube, ebenfalls ohne Parent, befindet sich an Position 3,3,30.
Kann die Kamera den Cube sehen? Nun, starten wir die situation1.bb sehen wir mit bloßem Auge das die Kamera das Objekt sieht. Dies wollen wir aber mittels LinePick überprüfen. Wir erweitern die Situation1.bb um die Funktion LpickObject$().

Wichtig bei Situation 1:
1. Der Cube muss mittels „EntityPickMode“ einen Wert erhalten da er sonst mit keiner Pickmethode gefunden werden kann.
2. Da die Funktion die wir erstellen den Namen des Entitys zurück gibt muss der Cube mittels „EntityName“ einen Namen erhalten. (Type String)
3. Da die Funktion einen String zurückgeben soll, muss der Funktionsname mit einem „$“ Zeichen enden. Wird dies vergessen wird bei einem Return eine Zahl zurückgegeben.



Code: [AUSKLAPPEN]
AppTitle "LinePick komplett enträtselt by bob. Situation 1"
Graphics3D 800,600,32
SetBuffer BackBuffer()

Cam = CreateCamera()
PositionEntity Cam, 1,1,1


Cube = CreateCube()
PositionEntity cube, 3,3,30
EntityPickMode cube,2
NameEntity cube,"Eine Cube"

Licht = CreateLight()
TurnEntity licht, 45,0,0

While Not KeyHit(1)
   Name$ =  LPickObject(Cam,Cube)
   UpdateWorld
   RenderWorld
   Text 0,0, "Linepick Objekt = "+ name$
   Flip
Wend


Function LpickObject$(Start,Ziel)
   sX# = entityX(Start)
   sY# = entityY(Start)
   sZ# = entityZ(Start)
   
   zX# = entityX(Ziel)
   zY# = entityY(Ziel)
   zZ# = entityZ(Ziel)
   
   ;Berechnen der Delta    Werte
   dX# = zX -sX
   dY# = zY -sY
   dZ# = zZ -sZ
   
   r# = 1.0

   DebugLog " "
   DebugLog "Cube (Ziel) Position xyz:" +zx +"|"  +zy +"|" +zz
   DebugLog "Kamera (Start) Position xyz:" +zx +"|"  +zy +"|" +zz
   DebugLog "Deltawerte zwischen Start und Ziel xyz: " + dx +"|"  +dy +"|" +dz


   Entity = linepick(sx,sy,sz,dx,dy,dz,r)
   If Entity <> 0 Then
      Return EntityName(entity)
   EndIf
End Function




Werfen wir einen genaueren Blick auf die LpickObject$() Funktion. Bei der Berechnung der Deltawerte fällt auf das wir die die Startwerte von den Zielwerten Subtrahieren.
Versucht nie die Zielwerte von den Startwerten zu Subtrahieren. Die Deltawerte sind also lediglich Entfernungen zwischen den einzelnen Schwerpunkten der Objekte. D.h. das die Deltawerte auch im negativen Bereich liegen können. Würden wir die Kamera auf z.B. 50,50,50 verschieben, wären alle Deltawerte negativ. (-47,-47,-20). Wir würden weiterhin einen Leeren Bildschirm sehen da die Kamera nicht auf den Cube ausgerichtet ist. Unsere Funktion „sieht“ den Cube allerdings immer noch, was auch völlig richtig ist da die Linepick Funktion das erste Objekt zurückliefert was sich zwischen Startwert und Deltawerten befindet und gepickt werden kann. (EntityPickMode nie vergessen).


Situation 2: Ja wo laufen sie den?

Unser Cube erhält nun eine Parent Objekt. Wir verwenden dafür einen weitern Cube um das Parentobjekt sichtbar zu halten. Das Parent erhält die Koordinaten 2.5 , 2.5 ,29.
Die Funktion LpickObject$() gibt im Debugfenster nun auch permanent die mittels EntityX (Y,Z) ermittelten Positionsangaben des Cubes aus.
Starten wir das Programm Situation 2 sehen wir dass das Child vom Parent fast vollständig verdeckt wird (dies hat aber keinen Einfluss auf die LinePick Funktion) und das unsere Funktion den Cube nicht findet obwohl sich scheinbar nichts an den Positionsangaben verändert hat. Die errechneten Deltawerte sind die gleichen wie in Situation 1 und damit leider auch völlig falsch. Durch drücken der Leertaste kann die Positionsberechnung zwischen Absoluten und Relativen Koordinaten umgeschaltet werden.

Code: [AUSKLAPPEN]

AppTitle "LinePick komplett enträtselt by bob. Situation 2"
Graphics3D 800,600,32
SetBuffer BackBuffer()

Const Space_Key = 57

Cam = CreateCamera()
PositionEntity Cam, 1,1,1

Parent = CreateCube()
PositionEntity Parent, 2.5,2.5,29
EntityColor Parent,255,0,0

Cube = CreateCube(Parent)
PositionEntity cube, 3,3,30
EntityPickMode cube,2
NameEntity cube,"Eine Cube"

Licht = CreateLight()
TurnEntity licht, 45,0,0

Global AbsolutRelativ

While Not KeyHit(1)
   Name$ =  LPickObject(Cam,Cube)
   If KeyHit (Space_Key) Then AbsolutRelativ = 1- AbsolutRelativ
   UpdateWorld
   RenderWorld
   Text 0,0, "Linepick Objekt = "+ name$
   Text 0,20, "Leertaste zum Umschalten"
   Flip
Wend


Function LpickObject$(Start,Ziel)

   
   sX# = entityX(Start)
   sY# = entityY(Start)
   sZ# = entityZ(Start)
   
   zX# = entityX(Ziel,AbsolutRelativ)
   zY# = entityY(Ziel,AbsolutRelativ)
   zZ# = entityZ(Ziel,AbsolutRelativ)
   
   ;Berechnen der Delta    Werte
   dX# = zX -sX
   dY# = zY -sY
   dZ# = zZ -sZ
   
   r# = 1.0

   DebugLog " "
   DebugLog "Deltawerte xyz: " + dx +"|"  +dy +"|" +dz
   DebugLog "Cube Position xyz:" +zx +"|"  +zy +"|" +zz

   Entity = linepick(sx,sy,sz,dx,dy,dz,r)
   If Entity <> 0 Then
      Return EntityName(entity)
   EndIf
   
   
End Function



EntityX (Y,Z) richtig anwenden.
Die Lösung ist relativ simpel. Da das Parent Objekt an 2,2,29 erstellt wird, erhält das ChildObjeKt bei seiner Erstellung automatisch die Koordinaten des Parent Objekts.
D. h. der Cube wird ebenfalls an 2,2,29 erstellt. Da anschließend der Cube mittels PositionEntity auf 3,3,30 verschoben wird, sind seine Absolut gesehenen Koordinaten nun tatsächlich 5,5,59. Wollen wir mittels EntityX (Y,Z) die richtigen Koordinaten ermitteln müssen wir den Parameter 1 für Relative Koordinaten verwenden.
Das etwas verwirrende dabei ist das die LinePick Funktion mit Absoluten Daten gefüttert werden möchte und dies Absoluten Daten durch Angaben von Relativen Daten ermittelt wird. Der Parameter „1“ der EntityX (Y,Z) gibt also zunächst die X Position des Parent Objekts + die X Entfernung zwischen Parent und Child Objekt zurück. Somit erhalten wir einen absoluten Wert obwohl in der Hilfe (vollkommen zu Recht) der Parameter 1 als relativer Parameter erklärt wird.

Wir wissen als nun:
Ein Parent Objekt ist der lokale Schwerpunkt (Rotations- und Verschiebungs- Punkt) eines Child Objekts.

Der Lokale Schwerpunkt eines Objekts (Insbesondere bei selbst erstellten 3D Objekten) muss nicht zwangsweise im Zentrum des Objekts liegen.

Die EntityX (Y,Z) Funktion greift immer auf Schwerpunkt Koordinaten zu.

Fazit:
Sollen die Linepick Funktion eine Prüfung zwischen 2 Objekten durchführen muss gewährleistet sein das beim Ermitteln der Deltawerte tatsächlich auch die Gittenetz Struktur (Das Mesh) gepickt wird.



Situation 3: Verdeckte Ermittlung

Wir Erweitern unsere Situation2 um einige Konstanten. Weiterhin fügen wir eine Funktion hinzu die uns den Sehstrahl also die Koordinaten der LinePick Funktion auch optisch anzeigt. Function Zeige_Sehstrahl(). Wir verschieben den Funktionsaufruf LpickObject(Cam,Cube) zwischen Renderworld() und Flip in der Hauptschleife. Dies ist notwendig da unsere neue Funktion mit simpler 2D Technik arbeitet und diese nur angezeigt wir nachdem Renderworld ausgeführt wurde. (Lässt sich auch anders lösen aber wir wollen es hier nicht zu kompliziert machen.)
Dann packen wir in die Hauptschleife noch eine Tastatursteuerung um das Parent rotieren zu können.

Startet situation3 und benutzt die Cursortasten links und rechts um das Parent Objekt zu rotieren. Wir sehen deutlich das der Schwerpunkt des Parent (von der Kamera aus betrachtet) leicht verschoben ist. Schaltet wie gewohnt mit der Leertaste zwischen Absoluten und Relativen Koordinaten um und studiert wann nun endlich der Cube tatsächlich verdeckt wird. Ein bisschen davon ist allerdings noch immer zu sehen. Aber wir wissen ja dass nur auf den Schwerpunkt geachtet wird.

Um einen „Diablo“ Effekt zu erreichen setzen wir den Alpha Wert des Parent Objekts etwas herunter sobald das Parent gepickt wird.


Code: [AUSKLAPPEN]

AppTitle "LinePick komplett enträtselt by bob. Situation 3"
Const ScreenX = 800
Const ScreenY = 600
Graphics3D ScreenX,ScreenY,32
SetBuffer BackBuffer()

Const Space_Key = 57
Const Cursor_Left = 203
Const Cursor_Right = 205



Cam = CreateCamera()
PositionEntity Cam, 1,1,1

Global Parent = CreateCube()
PositionEntity Parent, 2.5,2.5,29
EntityPickMode Parent,2
NameEntity Parent,"Parent von Cube"
EntityColor Parent,255,0,0

Global oldAlpha = 1.0


Cube = CreateCube(Parent)
PositionEntity cube, 3,3,30
EntityPickMode cube,2
NameEntity cube,"Eine Cube"

Licht = CreateLight()
TurnEntity licht, 45,0,0

Global AbsolutRelativ

While Not KeyHit(1)
   If KeyHit (Space_Key) Then AbsolutRelativ = 1- AbsolutRelativ
   If KeyDown (Cursor_Left)    Then TurnEntity Parent, 0,+1,0
   If KeyDown (Cursor_Right) Then TurnEntity Parent, 0,-1,0
   UpdateWorld
   RenderWorld
   Name$ =  LPickObject(Cam,Cube)
   Text 0,0, "Linepick Objekt = "+ name$
   Text 0,20, "Leertaste zum Umschalten: Cursor links rechts für Rotation"
   Flip
Wend


Function LpickObject$(Start,Ziel)
   sX# = entityX(Start)
   sY# = entityY(Start)
   sZ# = entityZ(Start)
   
   zX# = entityX(Ziel,AbsolutRelativ)
   zY# = entityY(Ziel,AbsolutRelativ)
   zZ# = entityZ(Ziel,AbsolutRelativ)
   
   ;Berechnen der Delta    Werte
   dX# = zX -sX
   dY# = zY -sY
   dZ# = zZ -sZ
   
   r# = 1.0

   DebugLog " "
   DebugLog "Deltawerte xyz: " + dx +"|"  +dy +"|" +dz
   DebugLog "Cube Position xyz:" +zx +"|"  +zy +"|" +zz

   Zeige_SehStrahl(start,zX,zY,zZ)
   Entity = linepick(sx,sy,sz,dx,dy,dz,r)

   
   If Entity <> 0 Then
      If EntityName(entity) ="Parent von Cube" Then
            EntityAlpha Parent,0.3      
      Else
         EntityAlpha Parent,1
      EndIf
      
      Return EntityName(entity)
   EndIf
   
   
End Function


Function Zeige_SehStrahl(Cam,zx#,zy#,zz#)
   CameraProject Cam, zx#, zy#, zz#
   pZX = PROJECTEDX()
   pZY = PROJECTEDY()

   Rect ScreenX / 2 -200, ScreenY / 2 -100, 400, 200,0
   
   Line ScreenX / 2 -200, ScreenY / 2 -100 ,pZX,pZY
   Line ScreenX / 2 +200, ScreenY / 2 -100 ,pZX,pZY   
   Line ScreenX / 2 -200, ScreenY / 2 +100 ,pZX,pZY
   Line ScreenX / 2 +200, ScreenY / 2 +100 ,pZX,pZY
End Function


Ich hoffe, allen die bisher Schwierigkeiten mit der Linepick Funktion hatten, den Lösungsweg ein Stückchen näher gebracht zu haben. Weiter Codebeispiele folgen nun nicht mehr. Allerdings möchte ich nochmals auf ein paar wichtige Punkte aufmerksam machen.

Fertige 3D Modelle und „Hühneraugen“
Die Sniper Situation:
In eurem Game liegt ein Scharfschütze auf einem Dach und sucht mit seinem Zielfernrohr das Gelände ab. Taucht ein Feind im Sichtbereich auf schießt der Sniper.
Ragen die Füße des Snipers nun über das Dach hinaus und ist der Schwerpunkt an dieser Stelle hat der Sniper „Hühneraugen“. Er schießt auf alles was seine Füße sehen.

Stellt bei fertigen 3D Modellen, die ihr nicht selbst erstellt habt, erst einmal fest wo genau sich der Schwerpunkt des Modells befindet. Oft ist es so dass der Schwerpunkt der X,Z Koordinaten im Zentrum liegt. Die Y Position ist allerdings meist bei 0 (An den Füßen) Dies wir deswegen so gemacht um beim laden des Modells das Modell direkt auf dem Boden zu platzieren (Wenn dieser ebenfalls bei 0 ist.) Wäre die Y Koordinate ebenfalls im exakten Zentrum des Modells würde es zur hälfte im Boden stecken. Wird also mittels EntityY(MeinModell) die entsprechende Koordinate ermittelt addiert ihr z.B. die halbe Höhe des Models hinzu.

Öffnungen in den Formen:
Stellt euch den Grossbuchstaben A als 3D Modell vor. Die LinePick Funktion würde ihn nicht als „Hindernis“ erkennen wen sein Schwerpunkt in der Mitte liegen würde , der EntityPickMode auf 2 (Mesh) eingestellt ist und sich dahinter ein weiters Objekt befindet das auf „Sichtbarkeit“ Überprüft wird. Meist ist es ja das Ziel von einem bestimmten Punkt aus gesehen zu überprüfen ob sich zwischen diesem Startpunkt und einem zu überwachenden Objekt ein Hindernis befindet. Dies kann gewollt sein (Wir schauen ja genau durch die dreieckige Öffnung des A’s und dort befindet sich kein Teil der Mesh Surface) oder eben auch nicht. Verwendet bei EntityPickMode daher die korrekte Einstellung z.B. 3 (Quader) um sicherzustellen das LinePick das A immer als Hindernis erkennt wenn sich das zu Prüfende Objekt dahinter befindet, und ihr Öffnungen ignorieren wollt.

Der Radius von LinePick
Vergesst nicht den Radius den LinePick Funktion euren Bedürfnissen anzupassen.
Oft kann es sehr hilfreich sein den Radius relativ „Dick“ einzustellen. So würde in o.g. Situation das A bei entsprechender Radius Einstellung und EntityPickMode 2 (Mesh wird geprüft) die Ränder Streifen.


Kombination von Funktionen:

In Situation 3 sehen wir dass der Cube auch dann gepickt wird wen er im Kamerafenster gar nicht zu sehen ist. Dies ist oft nicht erwünscht. Kombiniert dafür die LinePick Funktion mit der EntityInView Funktion:
Ändert dazu einfach mal die Funktion „Function LpickObject$(Start,Ziel)“ folgendermaßen ab. (Eine Zeile einfügen nach der Deklaration)

Code: [AUSKLAPPEN]

Function LpickObject$(Start,Ziel)
   If Not EntityInView(Ziel,Start) Then return


~~~~~Rest der Funktion wie bisher

Die Funktion wird also sofort verlassen wen das Zu überprüfende Objekt sich nicht im Projektionsbereich der Kamera befindet.

Viel Erfolg und viel Spass
(heute ist Weiberfastnacht und ich geh nun trinken. Wink )
bob
Er soll an den Spielen teilnehmen bis er spielend stirbt. MCP - TRON
  • Zuletzt bearbeitet von Bob am Fr, Feb 24, 2006 9:33, insgesamt einmal bearbeitet
 

Ava

Gast

BeitragDo, Feb 23, 2006 15:45
Antworten mit Zitat
Klasse geschrieben, Bob! Ich bin mir sicher, dass Du vielen Einsteigern damit eine grosse Hilfe sein wirst. Smile

Thema "Hühneraugen"

Hier möchte ich gern den Vorschlag unterbreiten, solche Probleme stets mit Pivots zu lösen. Das macht es wesendlich einfacher, und die Funktion bzw. Koordinaten müssen nicht ständig neu angepasst werden. Ein Pivot in Augenhöhe oder direkt vor die Waffe als Child an das eigentliche Modell "geheftet" (bei animierten Objekten: an einen passenden Bone) und schon klappt das wundebar, wenn man diesen dann für den LinePick nutzt. Smile


+ Ava +
 

c64

BeitragFr, Feb 24, 2006 15:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Code: [AUSKLAPPEN]


 If  EntityInView (player\mesh_id,camera)
  If  EntityVisible (camera,player\mesh_id)

      ;ENTITY ist in der Camera und wird nicht verdeckt !!!!!!!!

  endif
 endif




Wink

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group