Am I WTF with C#?



  • The last line was all the constructor contained "for now", when I created the file and wrote the basics of the class.

    Now I'm in the stage of refining it, and just modified it to look like this:

    public QuadNode()
            {
                //DO NOT DO THIS unless nodes will be filled in immediately
                //IsSolid, Normal, and other spatial functions depend on this array
                //being null all the time when it's of no use!
                //_nodes = new QuadNode[4];
            }
    

    ...then I stopped and wondered "is this a WTF piece? am I WTF?"

    So I'm asking.

    (a little background)

    It's a code from a 2d platformer game where all the level geometry should be generated and stored as a quad-tree, where each quad has info about the normal of its surface, and whether it's solid or empty, etc... this info is either hard-set in the quad, or "bubbled-up" from the child nodes, which are stored in the array the... (can you even call this) code (?) mentions...



  •  Its seems you want several methods for creating a node... so you either want method overloading for a factory class. Or have an interface that forces each object to have a method for creatign its node. Really can't tell with the amount of detail you are giving.

     However, since you are making a complext program, I very much suggest that you should write up a UML diagram for it. Software Patterns is a must-read and will really go long ways to improving the readability, re-usability and durability of your code if you use it.



  • Is QuadNode a class when it's supposed to be an interface? I don't get what you're doing here... I don't see how you'd ever have a non-static class (normally) with no constructor whatsoever.

    Why don't you give us the class definition? That'd be a lot more useful than a blank function.



  • @blakeyrat said:

    Why don't you give us the class definition? That'd be a lot more useful than a blank function.


    unfinished and untested so far, but as you wish, it might explain more:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    
    namespace Bullet.World
    {
        public class QuadNode : IDisposable
        {
            private bool _isSolid;
            public bool IsSolid
            {
                get
                {
                    if (_nodes == null) return _isSolid;
                    else
                    {
                        if (_nodes.Any(n => n.IsSolid)) return true;
                        else return false;
                    }
                }
    
                set //what should this actually do? set all children to solid? or delete all children and set itself to solid? more likely.
                {
                    _isSolid = value;
                    
                    foreach (QuadNode n in _nodes) n.Dispose();
                } 
            }
    
            private Vector2 _normal;
    
    
            private QuadNode[] _nodes;
            public QuadNode this[int id]
            {
                get {
                    if (_nodes != null) return _nodes[id]; //TOTEST: does this return null if array is initialized, but there's no item at requested id?
                    else return null; //shouldn't return self? so i have a fluent interface?
                }
                set {
                    if (_nodes == null) _nodes = new QuadNode[4]; 
                    
                    _nodes[id] = value;
                }
            }
    
            public QuadNode()
            {
                //DO NOT DO THIS unless nodes will be filled in immediately
                //IsSolid, Normal, and other spatial functions depend on this array
                //being null all the time when it's of no use!
                //_nodes = new QuadNode[4];
            }
    
            private GeometryTile _tile;
            public GeometryTile Tile
            {
                get { return _tile; }
            }
    
            public void Dispose()
            {
                //when you're disposed of, you take care of your children first
                if (_nodes != null) foreach (QuadNode n in _nodes) { if (n != null) { n.Dispose(); } }
                //then you take care of yourself (in this case I don't think we need to do anything (yet, at least))
                _nodes = null;
            }
        }
    }
    

    (i just work this way, I write up stubs of all the classes, for whole parts of the program structure, connect them together, and then gradually implement the details as I need them, this is still before the first compile, This QuadNode will be used by a QuadTree (well, it actually IS a quadtree already, ah, recursion, how we love you) class which will handle searching through it, returning "tiles" in the specified "max resolution", etc...)



  • @SEMI-HYBRID code said:

    public bool IsSolid
    {
    get
    {
    if (_nodes == null) return _isSolid;
    else
    {
    if (_nodes.Any(n => n.IsSolid)) return true;
    else return false;
    }
    }

    hey, noticed a minor WTF. replaced with

     get
                {
                    if (_nodes == null) return _isSolid;
                    else return _nodes.Any(n => n.IsSolid);
                }
    


  • @SEMI-HYBRID code said:

    It's a code from a 2d platformer game ... where each quad has info about the normal of its surface

    [i]That[/i] sounds like a WTF. Why do you need normals in a 2D game?



  • 2.5d and/or sloped platforms, I'm guessing.



  • @pjt33 said:

    @SEMI-HYBRID code said:
    It's a code from a 2d platformer game ... where each quad has info about the normal of its surface

    That sounds like a WTF. Why do you need normals in a 2D game?

    to determine what angle to draw character sprites under, depending on where they're standing on the curved/sloped surface, to calculate bounce/jump angles, for calculating how well and for how long will a character be able to stay "stuck" to wall after jumping on it...



  • I'm not saying this is necessarily a good idea, but have you considered using an array of pointers, or even four individual pointers, instead of a pointer to an array?

    I'm having trouble seeing IsSolid as a simple boolean, since surely quads can be partially solid?



  • @Zecc said:

    I'm not saying this is necessarily a good idea, but have you considered using an array of pointers, or even four individual pointers, instead of a pointer to an array?

    Yes I have, but then I chose to go this way because it seemed more comfortable/natural to me. Why is it worth asking about? Wait... how do you even make that distinction in C#? Go on, declare me an array of pointers.

    @Zecc said:

    I'm having trouble seeing IsSolid as a simple boolean, since surely quads can be partially solid?

    ...kind of... in the first iteration, quads will be only solid or passable. A "virtual" slope will be created - the physics engine will look at the world with depth-1 resolution, and the last node of branch will generate & return "normal" of the surface calculated from how its leaf nodes are arranged (top left, bottom right and bottom left leafs solid, slope is 45°, return normal corresponding to that).



    In the second iteration i'd like to try to add the "partially solid", by having each quad store a funcion that "draws" its surface, and on which physics engine can calculate collision coordinates in a pretty cool way, I guess.



    Third iteration should be a system where you can convert from "approximated" surface stored in the function to actual quads purely with solid/passabe states.



    Maybe it's not clear from what I wrote so far, but... don't think of quadtree as a way of organizing space, but as a way to build level geometry of scalable detail/smoothness with it. Each leaf fquad is a square area with only the solid/nonsolid property (I'll add also some info about materials later.). Hmm... I could call it variable-size tile, that sounds cool :-D



  • @SEMI-HYBRID code said:

    @blakeyrat said:
    Why don't you give us the class definition? That'd be a lot more useful than a blank function.
    unfinished and untested so far, but as you wish, it might explain more:

     

    [Code snipped]
    

    (i just work this way, I write up stubs of all the classes, for whole parts of the program structure, connect them together, and then gradually implement the details as I need them, this is still before the first compile, This QuadNode will be used by a QuadTree (well, it actually IS a quadtree already, ah, recursion, how we love you) class which will handle searching through it, returning "tiles" in the specified "max resolution", etc...)

    Well, this kind of explains. You have a basic architecture failure. The fact that you cannot introduce consistent behavior for the IsSolid setter is a dead giveaway; time to refactor. I suggest you decouple the actual object from the QuadNode implementation. Introduce an IWorldObject interface, common to all world objects and refactor QuadNode so you can assign one or zero IWorldObject instances to it. (I.e. a QuadNode can have empty content and only serve as a branching node in the tree.) QuadNode will then only have a getter implementation for IsSolid and will enumerate over both its attached IWorldObject and its child QuadNode objects, while IWorldObject will have a getter and setter pair.

    Enumeration of the QuadNodes itself should probably work with an IEnumerable interface. (This means you won't be checking '== null' everywhere but can just check for 'empty' or use enumeration naturally without thinking about the 'null' special case.) Internal storage of the child nodes should probably just be a List<QuadNode> with an initial count passed to the constructor so that enough space is available for 4 nodes.



  • @Ragnax said:

    Well, this kind of explains. You have a basic architecture failure. The fact that you cannot introduce consistent behavior for the IsSolid setter is a dead giveaway; time to refactor. I suggest you decouple the actual object from the QuadNode implementation. Introduce an IWorldObject interface, common to all world objects and refactor QuadNode so you can assign one or zero IWorldObject instances to it. (I.e. a QuadNode can have empty content and only serve as a branching node in the tree.) QuadNode will then only have a getter implementation for IsSolid and will enumerate over both its attached IWorldObject and its child QuadNode objects, while IWorldObject will have a getter and setter pair.

    I don't think I'm doing what you think I'm doing. As I'm saying, I'm not using the quadtree the "standard" way, as a means to spacially organize/cathegorize objects, I'm trying to use the structure itself to serve as the static level geometry, and I want basically dynamic LOD, where each quad can tell about itself rough and quick info whether it's to be handled like a piece of solid geometry, OR it can probe its children and return more detailed info about the shape of it. I traverse the tree and each leaf (or the node at the max traverse depth) that IsSolid gets drawn as a platform, or a piece of it (depending on its shape and complexity) on a screen position and in size determined by its position and nesting within the quadtree.


    So when I'm directly setting IsSolid, I'm saying to that quad "i don't care about the hypothetical details of your surface, you've just become a wholly solid quad so you don't need no children!

    And when I'm setting a children for a quad, I'm saying to that quad "now I needed to add this detail to you, so you're not a wholly solid leaf quad anymore, next time I ask for your IsSolid data, take your children into consideration."


    Which isn't really possible in this bool case, but it reminds me that I need to do something similar when generating the quads normal vector, which will work kind of the same, either the quad has its own and it's a leaf quad (or we don't care about the added detail in its children), or it has children and in that case I need to return normal that takes those into consideration (will be different function, because in this case I need to exactly know the normal direction at any point of the quad's "surface").


  • Java Dev

    I think you're trying to simplify matters by having one object do two things. It may seem to work now, but I see this falling down around you once you need to store multiple properties (textures?).

    You've got a tree structure, and you've got things you're storing in it. You're trying to use one object for both. It isn't working.



  • @PleegWat said:

    I think you're trying to simplify matters by having one object do two things. It may seem to work now, but I see this falling down around you once you need to store multiple properties (textures?).

    You've got a tree structure, and you've got things you're storing in it. You're trying to use one object for both. It isn't working.

    Exactly.

    (Succinct and to the point. Nice summary. Probably what I should have posted in the first place when I was trying to make my point. )



  • Is LOD even necessary in a 2D game anyway? How large are these levels? Is there a gimmick like where the hero can shrink to 1" tall and everything's huge that you need LOD for?

    PleegWat is spot-on with his observation, what you want is a "IQuad" interface which both QuadTree and QuadNode have so you can deal with them interchangeably, then make QuadTree basically a List<> of QuadNodes plus whatever aggregation methods are needed to treat the collection as a single object. (Although God knows how you'll do that with a solid bool-- if it contains at least one solid QuadNode, then it's solid? Or does it have to contain 100% solid QuadNodes? There be issues there.)

    But also: this has the stink of premature optimization to me. Your target hardware isn't a Commodore 64. Just implement the thing in the simplest possible way and only bother to optimize if you have to. The min specs for ANYTHING that runs XNA is enough to run a City-of-Seattle-sized 2D platformer level without any performance hit whatsoever, plus plenty left over.



  • @blakeyrat said:

    what you want is a "IQuad" interface which both QuadTree and QuadNode have so you can deal with them interchangeably, then make QuadTree basically a List<> of QuadNodes plus whatever aggregation methods are needed to treat the collection as a single object.

    Yep. Basically what I also suggested: detach the quadtree nodes from the world objects themselves and have methods on the nodes that aggregate properties like `IsSolid` accordingly.


Log in to reply