Game Development Community

[RESOLVED] T2DSceneObjects as HUD elements

by Ron Barbosa · in Torque X 2D · 04/21/2010 (9:01 pm) · 28 replies

Hey all...I'm using some plain old scene objects to make up the gauges and meters of my HUD. Can anyone recommend a good way to keep them in a static position on the screen (IE always in the upper left corner)?

I tried mounting them to the player, but I can't set the offset in TXB.

Any help would be appreciated...thanks!
--RB
Page «Previous 1 2
#1
04/21/2010 (10:41 pm)
You could create a scene object that, every ProcessTick, sets its position using the camera's position and size to determine where you want the element.
#2
04/22/2010 (4:01 am)
Thanks, William. I guess I'll write a HUD component that tracks the camera's original size (my camera changes size) and scales/positions the HUD components as a function of the current position and the original size.

I guess I was hoping for some "trick" that was built into the engine for this type of thing.

Thanks again!
--RB
#3
04/22/2010 (5:17 am)
You can achieve this with multiple GUISceneview instances. Each view can have it's own camera. To add an extra scene view to the canvas simply set the scene view's folder...

myHudView.Folder = GUICanvas.Instance;

You can then load a scene and put it in the new scene view (i.e. set the sceneview's camera to the the hud scene camera).

IMPORTANT: make sure that all the objects in the scene for the hud have the correct scenegraph set or they will just get added to the main scene like before! (if you're using the modded version of the engine from the SVN then you should be fine so long as you have renamed the scenegraphs for any scenes already loaded before you loaded the hud scene - otherwise you will need to loop through the scene objects and set their scenegraph to the right one). Also make sure the scene's camera has the right scenegraph reference too.
#4
04/22/2010 (5:34 am)
@Duncan...you're about as knowledgeable as anyone I've seen around here when it comes to the TX products...out of curiosity...where do you get this information?

=P

Have you just been ripping into the engine or do have some magic tome of tutorials that you aren't sharing with the class? ;)

Thanks again...I'll give this a shot...although I'll admit I'm not 100% sure how all of this works or how to implement it.

I'll dig through it, though...thanks.

--RB
#5
04/22/2010 (6:05 am)
lol :) No I don't have any hidden tomes. I used to be in professional game dev full time though before I turned freelance. And I do have a substantial in-house framework that extends Torque X. A side effect of developing that was a greater knowledge of the engine.

GUICanvas and GUISceneview: the GUICanvas is a Folder so you can add as many sceneviews to it as you like. Torque defaults to just one active sceneview, but you can add more. Each sceneview should reference a camera. And each sceneview's camera should reference a scenegraph. So each sceneview will draw the objects in that scenegraph using that camera. (technically you could even render the same scenegraph twice in different sceneviews - e.g. for a splitscreen feature).

Much fun is to be had with multiple sceneviews and scenes :)
#6
04/22/2010 (6:13 am)
Here's a little class I use to manage sceneview layering more intuitively.

using System;
using System.Collections.Generic;
using System.Text;
using GarageGames.Torque.GUI;
using GarageGames.Torque.Core;

namespace A.Namespace
{
    /// <summary>
    /// Provides methods to insert GUISceneView instances in a controlled layered way on the
    /// GUICanvas.
    /// 
    /// NOTE: if comparing this with the what GUICanvas calls 'front' and 'back' be warned that
    /// GUICanvas is referring to positions in a list and not positions as they appear on a display
    /// (the exact opposite infact).
    /// </summary>
    public class SceneViewLayering
    {
        public enum EnumAbsolutePosition { Front, Back };


        public static void InsertInfrontOf(GUISceneview viewToInsert, GUISceneview viewToInsertInfrontOf)
        {
            viewToInsert.Folder = GUICanvas.Instance;

            if (viewToInsertInfrontOf != null)
            {
                GUICanvas.Instance.ReOrder(viewToInsertInfrontOf, viewToInsert);
            }
        }

        public static void InsertBehind(GUISceneview viewToInsert, GUISceneview viewToInsertBehind)
        {
            viewToInsert.Folder = GUICanvas.Instance;
            GUICanvas.Instance.ReOrder(viewToInsert, viewToInsertBehind);
        }

        public static void InsertInfrontOf(GUISceneview viewToInsert, EnumAbsolutePosition positionToInsertInfrontOf)
        {
            InsertInfrontOf(viewToInsert, GetViewAt(positionToInsertInfrontOf));
        }

        public static void InsertBehind(GUISceneview viewToInsert, EnumAbsolutePosition positionToInsertBehind)
        {
            InsertBehind(viewToInsert, GetViewAt(positionToInsertBehind));
        }

        public static void Remove(GUISceneview view)
        {
            view.Folder = TorqueObjectDatabase.Instance.RootFolder;
        }

        protected static GUISceneview GetViewAt(EnumAbsolutePosition position)
        {
            if (GUICanvas.Instance.GetNumObjects() == 0) return null;

            switch (position)
            {
                case EnumAbsolutePosition.Front:
                    return GUICanvas.Instance.GetObject(GUICanvas.Instance.GetNumObjects() - 1) as GUISceneview;

                case EnumAbsolutePosition.Back:
                    return GUICanvas.Instance.GetObject(0) as GUISceneview;

                default:
                    Assert.Fatal(false, "Unrecognized position: " + position);
                    return null;
            }
        }
    }
}


To add your hud sceneview you would do the following:

SceneViewLayering.InsertInfrontOf(myHudView, SceneViewLayering.EnumAbsolutePosition.Front);


Also note that if a sceneview is not on the canvas then, of course, it won't be rendered. Useful if you want to 'hide' a sceneview, but not delete it or it's contents.
#7
04/22/2010 (6:21 am)
And all of the sceneviews are part of the same .txscene file? Is that how this works? So I'd be building out my HUD in each level file (which is fine...if not ideal).

Or do you have the HUD in its own .txscene file?

Sorry...for the ignorance...I've been so wrapped up in getting MY code to work for my prototype, that I haven't really taken many opportunities to open up the TX code and really dig into it.

Just as a side note...I am in fact running the modified TX from the online svn repo. I'm mostly up to date, but I have not yet picked up the async scene loader.

Thanks again!
--RB
#8
04/22/2010 (6:53 am)
I would put the hud in its own .txscene file.


e.g.

1. Load level scene
2. Load hud scene
3. Create a sceneview for the hud
4. Set the hud view to use the hud scene (see prev posts)
5. Add the hud view to the canvas


Creating a new sceneview is as simple as:

GUISceneview myHudView = new GUISceneview();
myHudView.Name = "HudView";
myHudView.Style = new GUIStyle();
#9
04/22/2010 (7:02 am)
I see...that's much more elegant than the solution I thought I would have to implement.

Thanks for the clarification. Presumably all objects in both the HUD scene and the level scene are accessible to one another.

So as I perform my tick processing that manages the levels in my gauges, my player object (which holds the meter value) can query the object database for the gauge object (by name or whatever) and set the meter value (even though the gauge is defined in a different .txscene file).

Good information and guidance...this is gonna help me out hugely!

Thanks again!
--RB

#10
04/22/2010 (8:18 am)
Yes, the database allows you to access objects as you normally would regardless of which scenegraph or sceneview they are in.
#11
04/22/2010 (3:46 pm)
...or there's the easy and dirty method.
mount to camera:
create component with input for vector2 (this will be the offset of the hud element).
_sceneObject.position = bla bla bla camera.position + whatever u called the vector2 offset from the component.

...maybe add the display layer you want, to be entered into the component as well.


not the most elegant. but damn if its not easy to manage. :P
#12
04/22/2010 (4:28 pm)
Ron's camera also changes size, which complicates things a bit more. Once you get the hang of working with multiple sceneviews though, it's actually a lot simpler than it sounds :)
#13
04/23/2010 (4:45 am)
@Steve...I'm all for "easy if not elegant," but Duncan's right. My camera will change size, and I think it's a worthwhile exercise to learn how to work with multiple sceneviews.

At any rate...I've resolved most of the items on my weekend "to-do" list for my game, and it's not even Friday night yet...so I'm ahead of myself so far. I think I'll take the time to learn to use the multiple sceneviews this weekend.

Thanks for the help, gents!
--RB
#14
04/24/2010 (7:58 am)
@Duncan...if you're still following this thread, I'm working on using multiple scene views. Haven't gotten it down yet, but I've made some "progress."

I did have a couple of concerns. First, there is a bug in TXB that does not save the camera name to the .txscene file. So I think I will always have a conflict in the Torque object DB while searching for my camera since both the play scene and the HUD scene will both have a camera called "Camera" is this a correct assumption?

I've verified that if I do this:

camera = TorqueObjectDatabase.Instance.FindObject<T2DSceneCamera>("Camera");

Immediately after loading my HUD, then immediately again after loading my scene, I get 2 different camera objects (each with the respective settings of the HUD and play cameras.

Secondly...I can see my HUD...but the play field is blank.

I think I may have a problem with the sequence I'm using to load the level and HUD. You recommended level first, HUD later...but that triggers an assertion failure in my current setup (because my level has direct references to objects that I have now moved to my HUD scene).

Let me play around with that...but if you have any guidance for the camera name thing, that would be great.

[EDIT] I think it may be that the screen clearing color for my HUD needs to be set to something other than black. I need a transparent clearing color (I think). Let me find where that gets set.

Thanks
--RB
#15
04/24/2010 (8:10 am)
Order of loading the scenes doesn't matter for the sceneview thing. Sounds like you either don't have a second scene view set up or the camera is not set on it properly or the scenegraph is not set on the camera properly. That kind of thing - just a little missing link.


You can get the camera for a scene directly via the scene data object that SceneLoader.Load() returns. And while you're there you can get the scenegraph too (as you noted, querying the database is less than helpful when the objects have default naming applied by tx).


TorqueSceneData sceneData = Game.Instance.SceneLoader.Load(mylevel);

T2DSceneGraph sceneGraph = (T2DSceneGraph)sceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneGraph; });
T2DSceneCamera camera = T2DSceneCamera)sceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneCamera; });

camera.SceneGraph = sceneGraph;
mySceneView.Camera = camera;


Do that for each level you load into a new sceneview and things should be alot better. Also don't forget to add the sceneviews to the canvas (see prev posts).
#16
04/24/2010 (8:19 am)
Thanks again, Duncan. I think I may have oversimplified it just a bit. Here's my BeginRun method:

protected override void BeginRun()
{
    base.BeginRun();

    GuiPlay playGUI = new GuiPlay();
    GUICanvas.Instance.SetContentControl(playGUI);

    // Set up the HUD
    TorqueSceneData hud = SceneLoader.Load(@"data\levels\hud.txscene");
    GUISceneview hudSceneView = new GUISceneview();
    hudSceneView.Name = "HudView";            
    hudSceneView.Style = new GUIStyle();
    hudSceneView.SceneData = hud;
            
    // Load the scene data
    Game.Instance.SceneLoader.Load(@"data\levels\levelData.txscene");
            
    // Set the camera up
    T2DSceneCamera camera = TorqueObjectDatabase.Instance.FindObject<T2DSceneCamera>("Camera");
    camera.FarDistance = 5000.0f;

    // Attach the HUD
    hudSceneView.Folder = GUICanvas.Instance;
            
    // Mount the camera to the player
    T2DSceneObject player = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>("mainPlayer");

    // Ensure that both exist
    if (player != null && camera != null)
    {
        camera.Mount(player, "", false);
        camera.TrackMountRotation = false;
    }
}

I don't normally worry about setting the sceneGraph and Camera (I guess TX does that by default). So if I'm reading you correctly, because I have more than one loaded scene, I need to manually set the sceneGraph and Camera for each?

Thanks again for all your help here.
--RB
#17
04/24/2010 (9:28 am)
Yes, that's right - you need to set up the scenegraph/camera/sceneview stuff yourself, for each scene file that you load. It's best to make sure all the details are in place than rely to tx to guess right. Try the following modified version:

protected override void BeginRun()
{
    base.BeginRun();

    GuiPlay playGUI = new GuiPlay();
    GUICanvas.Instance.SetContentControl(playGUI);

    // Set up the HUD
    TorqueSceneData hud = SceneLoader.Load(@"data\levels\hud.txscene");
    GUISceneview hudSceneView = new GUISceneview();
    hudSceneView.Name = "HudView";
    hudSceneView.Style = new GUIStyle();

    T2DSceneGraph hudSceneGraph = (T2DSceneGraph)hud.SceneData.Find(delegate(object obj) { return obj is T2DSceneGraph; });  
    T2DSceneCamera hudCamera = (T2DSceneCamera)hud.SceneData.Find(delegate(object obj) { return obj is T2DSceneCamera; });
    hudCamera.SceneGraph = hudSceneGraph;
    hudSceneView.Camera = hudCamera;

    // Load the scene data
    TorqueSceneData playSceneData = Game.Instance.SceneLoader.Load(@"data\levels\levelData.txscene");

    T2DSceneGraph playSceneGraph = (T2DSceneGraph)playSceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneGraph; });
    T2DSceneCamera playCamera = (T2DSceneCamera)playSceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneCamera; });
    playCamera.SceneGraph = playSceneGraph;
    playGUI.Camera = playCamera;

    // Set the camera up
    playCamera.FarDistance = 5000.0f;
    hudCamera.FarDistance = 5000.0f;

    // Attach the HUD
    hudSceneView.Folder = GUICanvas.Instance;

    // Mount the camera to the player
    T2DSceneObject player = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>("mainPlayer");

    // Ensure that both exist
    if (player != null && playCamera != null)
    {
        playCamera.Mount(player, "", false);
        playCamera.TrackMountRotation = false;
    }
}




FYI: you don't need to to put hudSceneView.SceneData = hud; (this is an async loading thing that you don't need to bother with here)
#18
04/24/2010 (9:28 am)
OK...99.9% of the way there by manually managing the scene graphs and cameras. Thanks, Duncan.

The last artifact that I'm not sure how to manage is comes from lines 5 & 6 above.

This is part of the out-of-the-box T2D Starter Project with GUI. They create a small text overlay with Score, Health, etc. This is appearing BEHIND my main scene data.

Once I can resolve that, I'll be done. Not sure how this plays in, because it's not a txscene file, so it doesn't have a scene view or camera (to my knowledge).

[EDIT] We must have posted at the same time. My updated versions looks nearly identical to your edits. In my updated version I had attached "playGUI" to the HUD camera. Now I have it attached to the level camera and all is right with the world! Thanks again!

--RB
#19
04/24/2010 (9:52 am)
Try this:

protected override void BeginRun()
{
    base.BeginRun();

    // Set up the HUD
    TorqueSceneData hud = SceneLoader.Load(@"data\levels\hud.txscene");
    GuiPlay hudSceneView = new GuiPlay();
    hudSceneView.Name = "HudView";
    hudSceneView.Style = new GUIStyle();

    T2DSceneGraph hudSceneGraph = (T2DSceneGraph)hud.SceneData.Find(delegate(object obj) { return obj is T2DSceneGraph; });
    T2DSceneCamera hudCamera = (T2DSceneCamera)hud.SceneData.Find(delegate(object obj) { return obj is T2DSceneCamera; });
    hudCamera.SceneGraph = hudSceneGraph;
    hudSceneView.Camera = hudCamera;

    // Setup the level
    TorqueSceneData levelSceneData = Game.Instance.SceneLoader.Load(@"data\levels\levelData.txscene");
    GUISceneview levelSceneView = new GUISceneview();
    levelSceneView.Name = "LevelView";
    levelSceneView.Style = new GUIStyle();

    T2DSceneGraph levelSceneGraph = (T2DSceneGraph)levelSceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneGraph; });
    T2DSceneCamera levelCamera = (T2DSceneCamera)levelSceneData.SceneData.Find(delegate(object obj) { return obj is T2DSceneCamera; });
    levelCamera.SceneGraph = levelSceneGraph;
    levelSceneView.Camera = levelCamera;

    // Set the cameras up
    levelCamera.FarDistance = 5000.0f;
    hudCamera.FarDistance = 5000.0f;

    // Add the HUD and the level to the canvas
    levelSceneView.Folder = GUICanvas.Instance;
    hudSceneView.Folder = GUICanvas.Instance;

    // Mount the camera to the player
    T2DSceneObject player = TorqueObjectDatabase.Instance.FindObject<T2DSceneObject>("mainPlayer");

    // Ensure that both exist
    if (player != null && levelCamera != null)
    {
        levelCamera.Mount(player, "", false);
        levelCamera.TrackMountRotation = false;
    }
}


The thing to bear in mind is that there is no special treatment for the hud or the gameplay level scene. They are both just scene files being loaded and put in sceneviews. So mostly you just do the same thing for both. Unless you need something different, such as the Hud using a GuiPlay sceneview instead of a vanilla sceneview, it's pretty much all boiler plate stuff.
#20
04/24/2010 (10:00 am)
Save the sequence of some of the steps...this version looks exactly like mine.

You're awesome, Duncan...thanks for your help. It's working fine now.

--RB
Page «Previous 1 2