Guardare dall'alto il nostro terreno che rotea ci fa sentire un po' degli dei, ma sarebbe bello andare a dargli un'occhiata più
da vicino. Per questo, implementeremo una telecamera mobile che possa essere ruotata in ogni direzione con il mouse. Questo tipo di telecamera
di chiama mouse-camera e ha la particolarità di intrappolare il puntatore all'interno della finestra, poiché esso definisce
il punto verso cui si sta guardando (che, quindi, deve sempre essere al centro dello schermo).
Cominciamo a scrivere il codice che legga l'input del mouse e ruoti la telecamera:
PublicClass Game
'...
'TELECAMERA -------------------------------------
'Ricorda la precedente posizione del mousePrivate PrevMouseState As MouseState
'Queste variabili sono usate per memorizzare
'la rotazione su Y (LeftRight) e su X (UpDown)
'Essendo angoli, sono misurati in radiantiPrivate LeftRightRotation AsSinglePrivate UpDownRotation AsSingle
'Posizione della telecameraPrivate CameraPosition As Vector3
'Inoltre, consideriamo un'altra variabile:
'la velocità di rotazione:Private RotationSpeed AsSingle = 0.3
'...#Region "Gestione telecamera"
'Questa procedura calcola l'angolo di spostamento
'sulla base dello spostamento x-y del mouse
'sullo schermo. ElapsedTime è un parametro
'Single che rappresenta quanti secondi sono
'passati dal frame precedente. Infatti più
'tempo sarà passato e più la telecamera
'verrà ruotata (non su tutti i computer il
'programma gira a 60 frame per secondo!)PrivateSub ProcessMouseInput(ByVal ElapsedTime AsSingle)
'Ottiene lo stato del mouseDim CurrentMouseState As MouseState = Mouse.GetState
'Se il mouse è fermo, ossia la sua
'posizione è uguale a quella precedente,
'non fa niente. Altrimenti procedeIf CurrentMouseState <> PrevMouseState Then
'Calcola gli spostamenti su X e su YDim DX AsSingle = _
CurrentMouseState.X - PrevMouseState.X
Dim DY AsSingle = _
CurrentMouseState.Y - PrevMouseState.Y
'Modifica la rotazione sulla base di questi tre
'fattori
LeftRightRotation -= DX * RotationSpeed * ElapsedTime
UpDownRotation -= DY * RotationSpeed * ElapsedTime
'Come abbiamo già detto, il mouse deve sempre
'stare al centro della finestra, perciò lo
'rimettiamo in posizione
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
UpdateView()
EndIfEndSub
'Questo metodo ricalcola la matrice di vista sulla base della
'rotazione della telecameraPrivateSub UpdateView()
'Prima di tutto, creiamo una matrice di rotazione che
'rappresenti l'attuale stato della telecameraDim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
'Ora, ammettiamo di essere nella posizione iniziale e,
'quindi di guardare dritto davanti a noi. In questo
'caso il punto che guarderemmo sarebbe dato dalla
'somma della nostra posizione e del vettore "avanti"
'(ossia un vector3 con x=0, y=0 e z=-1). Allo
'stesso modo, il vettore "su" sarebbe il classico
'vettore con x=0, y=1 e z=0. Definiamo questi
'vettori:Dim Forward AsNew Vector3(0, 0, -1)
Dim Up AsNew Vector3(0, 1, 0)
'Tuttavia, dato che ora noi stiamo ruotati, anche questi
'vettori ci seguiranno nella rotazione. Quindi per
'ottenere i veri target e up vector bisogna ruotare
'quelli iniziali:Dim RotForward As Vector3 = _
Vector3.Transform(Forward, Rotation)
Dim RotUp As Vector3 = Vector3.Transform(Up, Rotation)
'Allora, il vettore "su" ruotato va bene così, ma
'il nostro punto-bersaglio non è ancora pronto:Dim Target As Vector3 = CameraPosition + RotForward
'Quindi reimposta la matrice
ViewMatrix = Matrix.CreateLookAt(CameraPosition, Target, RotUp)
EndSub#End Region
'...ProtectedOverridesSub LoadContent()
Shader = GetEffect(AppPath & "\SimpleShader.fx")
LoadHeightMap(GetTexture(AppPath & "\HeightMap.bmp"))
SetVertices()
SetIndices()
SetNormals()
CopyToBuffer()
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView( _
MathHelper.PiOver4, _
Me.GraphicsDevice.Viewport.AspectRatio, _
1, 500)
'Imposta la posizione della telecamera
CameraPosition = New Vector3(0, 40, TerrainLength / 2)
'Sposta il mouse al centro della finestra
Mouse.SetPosition(Me.GraphicsDevice.Viewport.Width / 2, _
Me.GraphicsDevice.Viewport.Height / 2)
'Imposta la posizione originale del mouse
PrevMouseState = Mouse.GetState
'Ricalcola la matrice di vista
UpdateView()
MyBase.LoadContent()
EndSub
'...ProtectedOverridesSub Update(ByVal GameTime As GameTime)
'Calcola la nuova direzione della telecamera
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
MyBase.Update(GameTime)
EndSub
'...EndClass
(Per uscire dal programma premete Alt+F4)
Ora siamo in grado di ruotare la telecamera per guardarci attorno. Che ne dite di muoverla?
#Region "Gestione telecamera"
'...
'Questa procedura legge i tasti premuti e sposta la
'telecamera di conseguenzaPrivateSub ProcessKeyboardInput()
'Questo vettore rappresenta lo spostamentoDim Motion AsNew Vector3(0, 0, 0)
Dim KeyState As KeyboardState = Keyboard.GetState
'Con W si va avantiIf KeyState.IsKeyDown(Keys.W) Then
Motion += New Vector3(0, 0, -1)
EndIf
'Con S si retrocedeIf KeyState.IsKeyDown(Keys.S) Then
Motion += New Vector3(0, 0, 1)
EndIf
'Con D ci si sposta a destraIf KeyState.IsKeyDown(Keys.D) Then
Motion += New Vector3(1, 0, 0)
EndIf
'Con A ci si sposta a sinistraIf KeyState.IsKeyDown(Keys.A) Then
Motion += New Vector3(-1, 0, 0)
EndIf
'Con Su ci si muove in altoIf KeyState.IsKeyDown(Keys.Up) Then
Motion += New Vector3(0, 1, 0)
EndIf
'Con Giù ci si muove in bassoIf KeyState.IsKeyDown(Keys.Down) Then
Motion += New Vector3(0, -1, 0)
EndIf
UpdatePosition(Motion)
EndSub
'...
'Ricalcola la nuova posizionePrivateSub UpdatePosition(ByVal Motion As Vector3)
'Ricordate che la telecamera è comunque ruotata
'perciò anche il vettore di movimento
'sarà ruotato di conseguenzaDim Rotation As Matrix = _
Matrix.CreateRotationX(UpDownRotation) * _
Matrix.CreateRotationY(LeftRightRotation)
Dim RotMotion As Vector3 = Vector3.Transform(Motion, Rotation)
CameraPosition += RotMotion
UpdateView()
EndSub#End Region
'...ProtectedOverridesSub Update(ByVal GameTime As GameTime)
'Calcola la nuova direzione della telecamera
ProcessMouseInput(GameTime.ElapsedGameTime.TotalSeconds)
ProcessKeyboardInput()
MyBase.Update(GameTime)
EndSub
'...