Inhaltsverzeichnis
Einleitung
Die atemberaubende Grafik von S5 lässt sich (meiner Meinung nach) mit den gegebenen Kameraeinstellungen nur sehr unzureichend auskosten. Die folgende Funktion ermöglicht es dem Spieler, während des Spieles zwischen der Standardsicht und einer rollenspieltypischen Schulterperspektive zu wechseln. In diesem Modus folgt die Kamera der selektierten Entität, die Sicht ist näher am Boden und Kipp- und Rotationswinkel lassen sich manuell verändern. Es dürften keine Komplikationen mit Briefings oder Cutscenes auftreten.
Im Endeffekt sieht die RPG-Sicht so aus:
Einbauen
Um die RPG-Sicht in seiner Map zu ermöglichen, muss der unten stehende Zusatzcode ins Skript kopiert werden.
Der Aufruf erfolgt dann folgendermaßen:
EnableRPGView( "NameEinerEntity" )
Dieser Skriptname gibt die Entität an, zur der die Kamera springt, wenn im RPG-Modus die selektierte Einheit stirbt.
Es sollte sich dabei also um eine „feste“ Entität handeln, beispielsweise das Hauptquartier oder ein Held.
Wenn diese Entity im Verlauf des Spieles geändert werden soll, kann man das mit…
gvRPG.hero = "NameDerNeuenEntity"
bewerkstelligen.
Wenn es in der Map ein PreludeBriefing oder eine einleitende Cutscene gibt, sollte die Initialisierungsfunktion erst danach; also am Besten in der Finished-Funktion aufgerufen werden.
Steuerung
Im Spiel kann die RPG-Sicht einfach per [Esc] an- und ausgeschaltet werden. In diesem Modus sind nun einige Funktionen (wie z.B. Speichern und Laden) nicht möglich, da sie zu Problemen führen können. Die neuen Funktionen zur Veränderung der Perspektive sind im Folgenden aufgelistet:
- [Einfg] dreht wie gewohnt die Kamera nach links; allerdings schwenkt sie nicht mehr automatisch zurück.
- [Entf] macht das gleiche rechtsrum.
- Mit [8] kann man im RPG-Modus nicht mehr die Armee 8 selektieren; hier ist die Taste dafür zuständig, die Kamera nach oben zu kippen. Das geht maximal bis man senkrecht auf den Beobachteten herunterschaut.
- [9] kippt entsprechend die Kamera nach unten. Die Untergrenze ist ein Winkel von 5° über dem Boden.
In Briefings und Cutscenes kann Escape wie gewohnt verwendet werden.
Code
Mit Kommentaren…
function EnableRPGView( _heroName ) -- Die RPG-Sicht darf nicht schon einmal erstellt worden sein... assert( not gvRPG, "RPGView ERROR: Attempt to setup RPG-View while already existing!" ); -- Der übergebene Name muss ein string sein... assert( type( _heroName ) == "string", "RPGView ERROR: Hero Name must be a string!" ); -- Setup des golbalen RPG-tables... gvRPG = { hero = _heroName, observedEntity = GetEntityId( _heroName ), zoomangle = 20, viewmode = "Normal", EscapeFunc = function() if gvRPG.viewmode == "Normal" then RPGView_SwitchToRPGView(); elseif gvRPG.viewmode == "RPG" then RPGView_SwitchToNormalView(); end end }; -- Speichere Backups der Ingame-Funktionen, die dieses Skript benutzt... GameCallback_GUI_SelectionChanged_OrigRPGView = GameCallback_GUI_SelectionChanged; GameCallback_Escape_OrigRPGView = GameCallback_Escape; QuickSave_OrigRPGView = QuickSave; QuickLoad_OrigRPGView = QuickLoad; GUIAction_ChangeBuildingMenu_OrigRPGView = GUIAction_ChangeBuildingMenu; GUIAction_JumpToBuildingOfWorker_OrigRPGView = GUIAction_JumpToBuildingOfWorker; GroupSelection_SelectTroops_OrigRPGView = GroupSelection_SelectTroops; StartBriefing_OrigRPGView = StartBriefing; EndBriefing_OrigRPGView = EndBriefing; if StartCutscene then StartCutscene_OrigRPGView = StartCutscene; end if CutsceneDone then CutsceneDone_OrigRPGView = CutsceneDone; end -- Erstelle eine neue Funktion, um die Kamera bei einem Selektionswechsel im RPG-Modus der neuen selektierten Entität folgen zu lassen... RPGView_ControlCamera = function() GameCallback_GUI_SelectionChanged_OrigRPGView(); if GUI.GetSelectedEntity() then gvRPG.observedEntity = GUI.GetSelectedEntity(); Camera.FollowEntity( gvRPG.observedEntity ); elseif IsAlive( gvRPG.observedEntity ) then local pos = GetPosition( gvRPG.observedEntity ); if ( type( pos ) == "table" ) and ( pos.X > 0 ) and ( pos.Y > 0 ) then Camera.FollowEntity( gvRPG.observedEntity ); else gvRPG.observedEntity = GetEntityId( gvRPG.hero ); Camera.FollowEntity( gvRPG.observedEntity ); end else gvRPG.observedEntity = GetEntityId( gvRPG.hero ); Camera.FollowEntity( gvRPG.observedEntity ); end end -- Überschreibe die Funktion <GameCallback_Escape>, um per [Esc] zwischen normalem und RPG-Modus wechseln zu können... GameCallback_Escape = gvRPG.EscapeFunc; -- Überschreibe die Funktion <QuickSave>, um Schnelles Speichern [F6] im RPG-Modus zu deaktivieren, da es zu Problemen führen kann... QuickSave = function() if gvRPG.viewmode == "Normal" then QuickSave_OrigRPGView(); elseif gvRPG.viewmode == "RPG" then Message("Das Spiel kann nur im normalen Modus gespeichert werden"); end end -- Überschreibe die Funktion <QuickLoad>, um Schnelles Laden [F7] im RPG-Modus zu deaktivieren, da es zu Problemen führen kann... QuickLoad = function() if gvRPG.viewmode == "Normal" then QuickLoad_OrigRPGView(); elseif gvRPG.viewmode == "RPG" then Message("Spielstände können nur im normalen Modus geladen werden"); end end -- Überschreibe die Funktion <GUIAction_ChangeBuildingMenu>, um das Wechseln zwischen den Gebäudemenüs im RPG-Modus zu verhindern, da die Kamera der selektierten Entität sonst nicht mehr folgt... GUIAction_ChangeBuildingMenu = function( _a ) if gvRPG.viewmode == "Normal" then GUIAction_ChangeBuildingMenu_OrigRPGView( _a ); elseif gvRPG.viewmode == "RPG" then Message("Diese Option steht nur im normalen Modus zur Verügung"); end end -- Überschreibe die Funktion <GUIAction_JumpToBuildingOfWorker>, um das Springen zu den Gebäuden eines Arbeiters im RPG-Modus zu verhindern, da die Kamera der selektierten Entität sonst nicht mehr folgt... GUIAction_JumpToBuildingOfWorker = function( _a ) if gvRPG.viewmode == "Normal" then GUIAction_JumpToBuildingOfWorker_OrigRPGView( _a ); elseif gvRPG.viewmode == "RPG" then Message("Diese Option steht nur im normalen Modus zur Verügung"); end end -- Überschreibe die Funktion <GroupSelection_SelectTroops>, um im RPG-Modus das Verändern des Kippwinkels der Kamera durch Drücken von [8] und [9] zu ermöglichen... GroupSelection_SelectTroops = function( _count ) if gvRPG.viewmode == "RPG" then if _count == 8 then if ( gvRPG.zoomangle+1 ) > 90 then gvRPG.zoomangle = 90; elseif ( gvRPG.zoomangle+1 ) < 5 then gvRPG.zoomangle = 5; else gvRPG.zoomangle = ( gvRPG.zoomangle+1 ); end Camera.ZoomSetAngle( gvRPG.zoomangle ); elseif _count == 9 then if ( gvRPG.zoomangle-1 ) > 90 then gvRPG.zoomangle = 90; elseif ( gvRPG.zoomangle-1 ) < 5 then gvRPG.zoomangle = 5; else gvRPG.zoomangle = ( gvRPG.zoomangle-1 ); end Camera.ZoomSetAngle( gvRPG.zoomangle ); else GroupSelection_SelectTroops_OrigRPGView( _count ); end elseif gvRPG.viewmode == "Normal" then GroupSelection_SelectTroops_OrigRPGView( _count ); else assert( false, "RPGView ERROR: Invalid Viewmode!" ); end end -- Überschreibe die Funktion <StartBriefing>, um vor einem Briefing den aktuellen Modus auf "Normal" umzustellen und die Standard [Esc]-Funktion zu aktivieren... StartBriefing = function( _briefing ) if gvRPG.viewmode == "RPG" then RPGView_SwitchToNormalView(); end GameCallback_Escape = GameCallback_Escape_OrigRPGView; StartBriefing_OrigRPGView( _briefing ); Display.SetRenderFogOfWar(0); end -- Überschreibe die Funktion <EndBriefing>, um nach einem Briefing wieder die RPG-spezifische [Esc]-Funktion zu aktivieren... EndBriefing = function( _briefing ) EndBriefing_OrigRPGView( _briefing ); Display.SetRenderFogOfWar(1); GameCallback_Escape = gvRPG.EscapeFunc; end -- Überschreibe die Funktion <StartCutscene>, um vor einer Cutscene den aktuellen Modus auf "Normal" umzustellen und die Standard [Esc]-Funktion zu aktivieren... if StartCutscene then StartCutscene = function( _Cutscene, _EscapeMode ) if gvRPG.viewmode == "RPG" then RPGView_SwitchToNormalView(); end Camera.StopCameraFlight(); GameCallback_Escape = GameCallback_Escape_OrigRPGView; StartCutscene_OrigRPGView( _Cutscene, _EscapeMode ); end end -- Überschreibe die Funktion <CutsceneDone>, um nach einer Cutscene wieder die RPG-spezifische [Esc]-Funktion zu aktivieren... if CutsceneDone then CutsceneDone = function() CutsceneDone_OrigRPGView() GameCallback_Escape = gvRPG.EscapeFunc; end end -- Vergrößere das Sichtfeld (beeinflusst nur den RPG-Modus), weil es so schöner aussieht... ;) for i = 1,10 do Camera_DecreaseFOV(); end -- ...Ende des Setup. end function RPGView_SwitchToNormalView() -- Dies ist die Funktion, die den Standard-Modus aktiviert. -- Sie wird aufgerufen wenn [Esc] im RPG-Modus gedrückt wird... assert( gvRPG.viewmode == "RPG", "RPGView ERROR: Attempt to toggle Normal view failed!" ); -- Aktiviere die Hauptmenü-Buttons zum Speichern und Laden von Spielständen... XGUIEng.ShowWidget( gvGUI_WidgetID.MainMenuWindow_SaveGame, 1 ); XGUIEng.ShowWidget( gvGUI_WidgetID.MainMenuWindow_LoadGame, 1 ); -- Deaktiviere den Himmel... Display.SetRenderSky(0); -- Aktiviere den Nebel des Krieges... Display.SetRenderFogOfWar(1); -- Aktiviere den Standard Kamera-Modus... Camera_ToggleDefault(); GUI.ClearNotes(); -- Benutze wieder die Original <GameCallback_GUI_SelectionChanged>-Funktion... GameCallback_GUI_SelectionChanged = GameCallback_GUI_SelectionChanged_OrigRPGView; -- ...Und deklariere den aktuellen Modus als "Normal". gvRPG.viewmode = "Normal"; end function RPGView_SwitchToRPGView() -- Dies ist die Funktion, die den RPG-Modus aktiviert. -- Sie wird aufgerufen wenn [Esc] im normalen Modus gedrückt wird... assert( gvRPG.viewmode == "Normal", "RPGView ERROR: Attempt to toggle RPG-view failed!" ); -- Deaktiviere die Hauptmenü-Buttons zum Speichern und Laden von Spielständen... XGUIEng.ShowWidget( gvGUI_WidgetID.MainMenuWindow_SaveGame, 0 ); XGUIEng.ShowWidget( gvGUI_WidgetID.MainMenuWindow_LoadGame, 0 ); -- Aktiviere den Himmel... Display.SetRenderSky(1); -- Deaktiviere den Nebel des Krieges... Display.SetRenderFogOfWar(0); -- Deaktiviere den Standard Kamera-Modus... Camera_ToggleDefault(); GUI.ClearNotes(); -- Wenn eine Entität selektiert ist, speichere diese als <observedEntity>... if GUI.GetSelectedEntity() then gvRPG.observedEntity = GUI.GetSelectedEntity(); end -- Lass die Kamera der <observedEntity> folgen... Camera.FollowEntity( gvRPG.observedEntity ); -- Stelle den RPG-Standardzoomabstand ein... Camera.ZoomSetDistance( 1200 ); -- Aktualisiere den aktuellen Kippwinkel, so dass man Camera.ZoomSetAngle( gvRPG.zoomangle ); -- Lass die Kamera der <observedEntity> "über die Schulter schauen"... Camera.RotSetAngle( Logic.GetEntityOrientation( gvRPG.observedEntity ) - 90 ); -- Benutze die RPG-spezifische Callback-Funktion anstatt <GameCallback_GUI_SelectionChanged>... GameCallback_GUI_SelectionChanged = RPGView_ControlCamera; -- ...Und deklariere den aktuellen Modus als "RPG". gvRPG.viewmode = "RPG"; end
…und in der platzsparenden Variante:
function EnableRPGView(_heroName) assert(not gvRPG,"RPGView ERROR: Attempt to setup RPG-View while already existing!") assert(type(_heroName)=="string","RPGView ERROR: Hero Name must be a string!") gvRPG={ hero=_heroName, observedEntity=GetEntityId(_heroName), zoomangle=20, viewmode="Normal", EscapeFunc=function() if gvRPG.viewmode=="Normal" then RPGView_SwitchToRPGView() elseif gvRPG.viewmode=="RPG" then RPGView_SwitchToNormalView() end end } GameCallback_GUI_SelectionChanged_OrigRPGView=GameCallback_GUI_SelectionChanged GameCallback_Escape_OrigRPGView=GameCallback_Escape QuickSave_OrigRPGView=QuickSave QuickLoad_OrigRPGView=QuickLoad GUIAction_ChangeBuildingMenu_OrigRPGView=GUIAction_ChangeBuildingMenu GUIAction_JumpToBuildingOfWorker_OrigRPGView=GUIAction_JumpToBuildingOfWorker GroupSelection_SelectTroops_OrigRPGView=GroupSelection_SelectTroops StartBriefing_OrigRPGView=StartBriefing EndBriefing_OrigRPGView=EndBriefing if StartCutscene then StartCutscene_OrigRPGView=StartCutscene end if CutsceneDone then CutsceneDone_OrigRPGView=CutsceneDone end RPGView_ControlCamera=function() GameCallback_GUI_SelectionChanged_OrigRPGView() if GUI.GetSelectedEntity()then gvRPG.observedEntity=GUI.GetSelectedEntity() Camera.FollowEntity(gvRPG.observedEntity) elseif IsAlive(gvRPG.observedEntity)then local pos=GetPosition(gvRPG.observedEntity) if(type(pos)=="table")and(pos.X>0)and(pos.Y>0)then Camera.FollowEntity(gvRPG.observedEntity) else gvRPG.observedEntity=GetEntityId(gvRPG.hero) Camera.FollowEntity(gvRPG.observedEntity) end else gvRPG.observedEntity=GetEntityId(gvRPG.hero) Camera.FollowEntity(gvRPG.observedEntity) end end GameCallback_Escape=gvRPG.EscapeFunc QuickSave=function() if gvRPG.viewmode=="Normal" then QuickSave_OrigRPGView() elseif gvRPG.viewmode=="RPG" then Message("Das Spiel kann nur im normalen Modus gespeichert werden") end end QuickLoad=function() if gvRPG.viewmode=="Normal" then QuickLoad_OrigRPGView() elseif gvRPG.viewmode=="RPG" then Message("Spielstände können nur im normalen Modus geladen werden") end end GUIAction_ChangeBuildingMenu=function(_a) if gvRPG.viewmode=="Normal" then GUIAction_ChangeBuildingMenu_OrigRPGView(_a) elseif gvRPG.viewmode=="RPG" then Message("Diese Option steht nur im normalen Modus zur Verügung") end end GUIAction_JumpToBuildingOfWorker=function(_a) if gvRPG.viewmode=="Normal" then GUIAction_JumpToBuildingOfWorker_OrigRPGView(_a) elseif gvRPG.viewmode=="RPG" then Message("Diese Option steht nur im normalen Modus zur Verügung") end end GroupSelection_SelectTroops=function(_count) if gvRPG.viewmode=="RPG" then if _count==8 then if(gvRPG.zoomangle+1)>90 then gvRPG.zoomangle=90 elseif(gvRPG.zoomangle+1)<5 then gvRPG.zoomangle=5 else gvRPG.zoomangle=(gvRPG.zoomangle+1) end Camera.ZoomSetAngle(gvRPG.zoomangle) elseif _count==9 then if(gvRPG.zoomangle-1)>90 then gvRPG.zoomangle=90 elseif(gvRPG.zoomangle-1)<5 then gvRPG.zoomangle=5 else gvRPG.zoomangle=(gvRPG.zoomangle-1) end Camera.ZoomSetAngle(gvRPG.zoomangle) else GroupSelection_SelectTroops_OrigRPGView(_count) end elseif gvRPG.viewmode=="Normal" then GroupSelection_SelectTroops_OrigRPGView(_count) else assert(false,"RPGView ERROR: Invalid Viewmode!") end end StartBriefing=function(_briefing) if gvRPG.viewmode=="RPG" then RPGView_SwitchToNormalView() end GameCallback_Escape=GameCallback_Escape_OrigRPGView StartBriefing_OrigRPGView(_briefing) Display.SetRenderFogOfWar(0) end EndBriefing=function(_briefing) EndBriefing_OrigRPGView(_briefing) Display.SetRenderFogOfWar(1) GameCallback_Escape=gvRPG.EscapeFunc end if StartCutscene then StartCutscene=function(_Cutscene,_EscapeMode) if gvRPG.viewmode=="RPG" then RPGView_SwitchToNormalView() end Camera.StopCameraFlight() GameCallback_Escape=GameCallback_Escape_OrigRPGView StartCutscene_OrigRPGView(_Cutscene,_EscapeMode) end end if CutsceneDone then CutsceneDone=function() CutsceneDone_OrigRPGView() GameCallback_Escape=gvRPG.EscapeFunc end end for i=1,10 do Camera_DecreaseFOV() end end function RPGView_SwitchToNormalView() assert(gvRPG.viewmode=="RPG","RPGView ERROR: Attempt to toggle Normal view failed!") XGUIEng.ShowWidget(gvGUI_WidgetID.MainMenuWindow_SaveGame,1) XGUIEng.ShowWidget(gvGUI_WidgetID.MainMenuWindow_LoadGame,1) Display.SetRenderSky(0) Display.SetRenderFogOfWar(1) Camera_ToggleDefault() GUI.ClearNotes() GameCallback_GUI_SelectionChanged=GameCallback_GUI_SelectionChanged_OrigRPGView gvRPG.viewmode="Normal" end function RPGView_SwitchToRPGView() assert(gvRPG.viewmode=="Normal","RPGView ERROR: Attempt to toggle RPG-view failed!") XGUIEng.ShowWidget(gvGUI_WidgetID.MainMenuWindow_SaveGame,0) XGUIEng.ShowWidget(gvGUI_WidgetID.MainMenuWindow_LoadGame,0) Display.SetRenderSky(1) Display.SetRenderFogOfWar(0) Camera_ToggleDefault() GUI.ClearNotes() if GUI.GetSelectedEntity()then gvRPG.observedEntity=GUI.GetSelectedEntity() end Camera.FollowEntity(gvRPG.observedEntity) Camera.ZoomSetDistance(1200) Camera.ZoomSetAngle(gvRPG.zoomangle) Camera.RotSetAngle(Logic.GetEntityOrientation(gvRPG.observedEntity)-90) GameCallback_GUI_SelectionChanged=RPGView_ControlCamera gvRPG.viewmode="RPG" end
Es werden keine zusätzlichen Comfort-Funktionen benötigt.