HUD(Canvas)


DefaultProperties
{
     bShowDebugMenu= false
     CurrentPage=- 1
     CurrentIndex= 0
 
     Pages( 0 )=(PageName= "General" ,PageCommands[ 0 ]=(CmdName= "Toggle Debug Camera" ,Command= "ToggleDebugCamera" ))
     Pages( 1 )=(PageName= "Debug Info" ,PageCommands[ 0 ]=(CmdName= "Turn off Debug Info" ,Command= "showdebug none" ),
 
PageCommands[ 1 ]=(CmdName= "Toggle Pawn Debug Info" ,Command= "showdebug pawn" ),
PageCommands[ 2 ]=(CmdName= "Toggle Camera Debug Info" ,Command= "showdebug camera" ),
PageCommands[ 3 ]=(CmdName= "Toggle Pawn Weapon Info" ,Command= "showdebug weapon" ))
     Pages( 2 )=(PageName= "HUD" ,PageCommands[ 0 ]=(CmdName= "Toggle HUD" ,Command= "ToggleHUD" ))
}


Now, for the various responses to input (Read the article on Input commands to see how to bind functions to keys.):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
exec function ToggleDebugMenu()
{
     CurrentIndex = 0 ;
     CurrentPage = - 1 ;
     bShowDebugMenu = !bShowDebugMenu;
 
     SetCinematicMode(bShowDebugMenu, false , false , true , true , true );
}
 
exec function NextMenuItem()
{
     local int maxIndex;
 
     if (bShowDebugMenu)
     {
         //Only do stuff when we actually are in the menu.
         if (CurrentPage != - 1 )
         {
             maxIndex = Pages[CurrentPage].PageCommands.Length- 1 ;
         }
         else
         {
             maxIndex = Pages.Length- 1 ;
         }
 
         CurrentIndex = Min(CurrentIndex + 1 , maxIndex);
     }
}
 
exec function PreviousMenuItem()
{
     if (bShowDebugMenu)
     {
         CurrentIndex = Max(CurrentIndex - 1 , 0 );
     }
}
 
exec function DoDebugCommand()
{
     local SandboxDebugCommand command;
 
     if (bShowDebugMenu)
     {
         if (CurrentPage != - 1 )
         {
             //Leave the menu and execute the selected command
             command = Pages[CurrentPage].PageCommands[CurrentIndex];
             ToggleDebugMenu();
             ConsoleCommand(command.Command);
         }
         else
         {
             //Change page
             CurrentPage = CurrentIndex;
             CurrentIndex = 0 ;
         }
     }
}
 
exec function DebugMenuBack()
{
 
     if (bShowDebugMenu)
     {
         if (CurrentPage != - 1 )
         {
             //Get back to the top level.
             CurrentPage = - 1 ;
             CurrentIndex = 0 ;
         }
         else
         {
             //We're at the top level: leave
             ToggleDebugMenu();
         }
     }
}

All that is left to do is the drawing of the menu:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function DrawDebugMenu(HUD H)
{
     local float XL, YL, YPos;
     local SandboxDebugCommand command;
     SandboxDebugCommandPage page;
     local int array_index;
     local Color command_color;
 
     if (bShowDebugMenu)
     {
         H.Canvas.Font = class 'Engine' .Static.GetLargeFont();
         H.Canvas.StrLen( "X" , XL, YL);
         YPos = 0 ;
         H.Canvas.SetPos( 0 , 0 );
         H.Canvas.SetDrawColor( 10 , 10 , 10 , 128 );
         H.Canvas.DrawRect(H.Canvas.SizeX,H.Canvas.SizeY); //Adding a dark overlay to visually notify we're in the menu.
 
         if (CurrentPage == - 1 )
         {
             SandboxHud(H).DrawTextSimple( "Debug Menu" ,vect2d( 0 ,YPos), H.Canvas.Font,H.WhiteColor);
             YPos += YL;
 
             foreach Pages(page,array_index)
             {
                 if (array_index == CurrentIndex)
                 {
                     command_color = H.GreenColor;
                 }
                 else
                 {
                     command_color = H.WhiteColor;
                 }
 
                 SandboxHud(H).DrawTextSimple(array_index$ ":" @page .PageName,vect2d( 0 ,YPos), H.Canvas.Font,command_color);
                 YPos += YL;
             }
         }
         else
         {
             page = Pages[CurrentPage];
             SandboxHud(H).DrawTextSimple( "Debug Menu - " $page.PageName,vect2d( 0 ,YPos), H.Canvas.Font,H.WhiteColor);
             YPos += YL;
 
             foreach page.PageCommands(command,array_index)
             {
                 //I know, I could have put that in a function as the body of the loop is the same as above.
 
                 if (array_index == CurrentIndex)
                 {
                     command_color = H.GreenColor;
                 }
                 else
                 {
                     command_color = H.WhiteColor;
                 }
 
                 SandboxHud(H).DrawTextSimple(array_index$ ":" @command .CmdName,vect2d( 0 ,YPos), H.Canvas.Font,command_color);
                 YPos += YL;
             }
         }
     }
}


We’re missing two things: A place where to call DrawDebugMenu() and the definition of DrawTextSimple():


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SandboxHUD extends UDKHUD;
 
event PostRender()
{
     super .PostRender();
     SandboxDebugMenu(PlayerOwner.CheatManager).DrawDebugMenu( self );
}
 
function DrawTextSimple( string text, Vector2D position, Font font,Color text_color)
{
     Canvas.SetPos(position.X,position.Y);
     Canvas.SetDrawColorStruct(text_color);
     Canvas.Font = font;
     Canvas.DrawText(text);
}


And we’re done!

Post-Rendered Actors

Complying with the idea of letting actors drawing the bit of HUD they’re responsible for, 

Unreal provides a really nice feature: the post-rendered actors. Basically, 

any Actor can draw things on the Canvas, provided that:

  1. The actor lets the HUD know it has something to draw on the HUD.
  2. The actor is actually rendered (i.e. it has at least one primitive component currently visible by the player’s camera).
  3. The HUD allows to draw actor overlays.

To illustrate this, I’ll use a custom actor which is a Note you place in your level and will show text on screen when you look at it.

1
2
3
4
class Sandbox3DNote extends Actor placeable ;
 
var () string Title;
var () string Text;

Registering a PostRendered Actor

To do this, an actor must call the HUD’s AddPostRenderedActor() and pass itself as the function’s parameter.

Now, the potentially tricky part is that as the HUD is owned by the PlayerController, you need a reference to it.

 Fortunately, the Actor class has a function called GetALocalPlayerController() which will get the first PlayerController it can find. 

In local multiplayer (same computer) it shouldn’t really matter which Controller you get as it’ll end up on the same screen :)

However, you can’t use that function at any moment. Namely, an actor’s PostBeginPlay is out of the question as you can’t say for sure the PlayerController will be created.

So if you don’t have access to the HUD or its PlayerController, a workaround is to create a state in your Actor and register it at the beginning of that state:

1
2
3
4
5
6
auto state Initialize
{
Begin :
 
     GetALocalPlayerController().myHUD.AddPostRenderedActor( self );
}

It sounds like a very dodgy method (mostly because I don’t exactly know when the actor’s states are started),

 but it does seem to work. Of course, when the actor has a direct reference to the PlayerController (such as weapons or vehicles), 

you don’t need to do that complicated stuff and PostBeginPlay should be a safe place to register.

Implementing the drawing function

The function which will be called (by native code) is Actor.PostRenderFor(). It provides the following parameters:

  • PlayerController PC: The Controller whose HUD you’ll be drawing on.
  • Canvas Canvas: The Canvas you’ll be drawing on.
  • Vector CameraPosition: Position of the controller’s camera.
  • Vector CameraDir: direction of the controller’s camera.

It might be important to know who we’re drawing for. For instance, in a team-based game, 

maybe we only need to draw an actor overlay for only one of the teams.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
simulated event PostRenderFor(PlayerController PC, Canvas Canvas, vector CameraPosition, vector CameraDir)
{
     local Vector vec;
     local Font previous_font; //We store the font and restore it at the end, otherwise it will mess up the drawing of the rest of the HUD.
 
     previous_font = Canvas.Font;
     Canvas.Font = class 'Engine' .Static.GetMediumFont();
     vec = Canvas.Project(Location); //This wil transform the 3D position to screen space-coordinates.
     Canvas.SetPos(vec.X,vec.Y);
     Canvas.SetDrawColor( 255 , 255 , 255 , 255 );
     Canvas.DrawText(Title, true );
     Canvas.DrawText(Text);
     Canvas.Font = previous_font;
}


And to finish, there are a few properties we need to set:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DefaultProperties
{
     Begin Object Class=SpriteComponent Name=Sprite
         Sprite=Texture2D 'EditorResources.S_Note'
         AlwaysLoadOnClient= False
         AlwaysLoadOnServer= False
     End Object
 
     Components.Add(Sprite)
 
     bAlwaysRelevant= true
     bNoDelete= true
     bMovable= false
}

Note that the SpriteComponent doesn’t have HiddenGame set to true. Because if it did, 

the actor wouldn’t have anything to render, and thus PostRenderFor() would never get called.

Also, in the HUD class, we need to make sure bShowOverlays is set to true (in its default properties, or wherever it’s appropriate).

If you’re having problems seeing this working, make sure the HUD is actually enabled (try the ToggleHUD console command).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值