Something went wrong. Try again later

PeezMachine

This user has not updated recently.

752 63 20 24
Forum Posts Wiki Points Following Followers

SummerJammers Update #2: Components All the Way Down

(If you're not sure what subclasses and interfaces are, I've included some helpful definitions at the end of the post)

Welcome back! I've had two weeks to work toward some milestones, so let's start by seeing how that went.

- straight toss in player-determined direction

Incomplete. Disc implementation was pushed back to the next deadline.

- player "dash"

Done!

- basic player/disc hit detection

Incomplete.

- simple "incidence = reflection" disc physics

Designed but not tested since I haven't written any objects that implement the CanDeflectDiscs interface, such as walls.

Best Bug: By accidentally asking for my SceneManager service instead of the SpriteBatch service, I managed to create a fun little Draw loop that went: SceneManager --> Scene --> Player --> SceneManager --> ...

It would be easy to look at the lineup there and say "one and a half out of four is pretty weak," but it's actually been a remarkably productive two weeks. To understand just how much progress has been made, we need to talk a bit about XNA's "component" architecture. There are two component classes defined in XNA, GameComponent and DrawableGameComponent. A GameComponent is essentially just an object that can be "enabled" when it needs to interact with the game logic and "disabled" otherwise (the DrawableGameComponent inherits those capabilities but adds members to determine if it should be drawn or not). The components contain event handlers that get called when an object is enabled/disabled or shown/hidden, but but there's actually not much else to them. The actual Update and Draw methods are left blank, since it's obviously up to each particular component to determine how it interacts with the game and draws itself to the screen. There's nothing saying you have to use XNA's components, but they get the job done and having the event handlers all written is pretty nice, so I've been using them in lieu of writing my own component classes... kind of.

You see, there's a complication. XNA also contains a GameComponentCollection class which can group components to manage relative update and draw orders. So, for example, if I want to ensure that the players get drawn after the court (so that you can actually see them), I could throw the court and both players into a collection with the appropriate DrawOrder values and they will be sorted automatically. This is some pretty sweet functionality, but there's a huge catch. The type of things that are allowed to be stored in these collections are any objects that implement the IGameComponent interface, namely GameComponent, DrawableGameComponent, or any subclasses I write. The collection itself doesn't implement this interface, which means you can't have a GameComponentCollection full of GameComponentCollections. This is too bad because it locks out a really cool recursive implementation where a single Draw or Update call gets sent to a top-level object like a Scene and it trickles down, drawing chunks of objects in the correct order (and each individual object in correct order relative to its chunkmates).

So I toyed with a couple of solutions. My first idea involved subclassing GameComponentCollection to make a new class called SuperComponentCollection. I would make this component implement the IGameComponent interface, thus allowing it to store other collections as elements and give me the recursive functionality I wanted. This was less than ideal for a number of reasons, so I instead decided to write a subclass of the SortedList class that would essentially store and sort chunks of components. It was at this point that I remembered something very important: just because a method is called Draw doesn't mean it has to directly put something on the screen. This lead me to a ridiculously simple solution: just allow each component to keep a list of sub-components. That way, when a component gets a Draw or Update call, it can just pass the call down the line to its children, who will pass it to their children until it finally gets to objects that literally appear on the screen (a compact class I call ImageComponent). So I subclassed the GameComponent and DrawableGameComponent classes into SuperComponent and SuperDrawableComponent and wrote (my first ever!) custom Comparer class that told each object how to sort its children by update and draw order. It works beautifully, with a single top-level Draw call trickling down.

The life of a Scene.Draw() call in my final implementation.Every object is calling its own Draw(), but only the pink ImageComponent objects actually draw to the screen when they do so.
The life of a Scene.Draw() call in my final implementation.Every object is calling its own Draw(), but only the pink ImageComponent objects actually draw to the screen when they do so.

This is big. When I first started with XNA a few weeks ago, it took me a matter of minutes to get something drawn to screen and to move it around with an X360 controller. But that was all hard-coded hack work. Now I'm pretty much in the same place - drawing some dummy art and moving it around with an X360 controller - but it's all going through the proper channels. What's happening on the screen isn't a tech demo, it's the game's actual logic running. In a similar vein, I also built a lot of the IO infrastructure that decouples input commands from the particular device, so while I'm using an X360 controller right now, any device that has a C# library can be supported.

I also feel like I'm "over the hump," so to speak. There's that moment with most computer-science related problems where you can finally make sense of what's happening each step of the way, and I feel like I've reached that point when it comes to the update and draw flow, which is the game in many ways. So it's been a lot of foundation-building lately, but I've got enough pieces in place that I should be able to start putting together a complete practice match without too much fuss. I've got the Stage, Disc, and Player classes in a pretty good place and have started doing some work on mapping on-court position (which is resolution-invariant) to absolute draw position (which is resolution-dependent). So let's go ahead and set the next goal. It'll be modest for a two-week goal, largely because I'm out of town (and away from my desktop) for over a week for Thanksgiving (which is also why there are no screenshots). That means I'll largely be writing code and pseudocode in Notepad until I can get home and add it properly, so I'm largely going to work on things that are self-contained or brand new.

By 5:00 PM PST on Monday, December 9, 2013

- Fully implement Wall, Disc, and Goal classes

- Build a complete test stage with goals, wall, and bleachers

- Fully implement methods to convert on-court position to draw position

- Implement Draw transforms and logic for 1920x1080 and 1280x800 resolutions (more will be added later, I just want to have two different resolutions for testing purposes while I build that framework)

In other news, the community overwhelmingly approved the name SummerJammers, so thanks to everyone who submitted names and voted. Assuming I hit the next milestone, I'd say the call for assets and extra development will go out by the end of the year. In the meantime, can y'all do me a favor and tell me what your native resolution is? I'd like to natively support any widely used resolutions, and I'd like to know what they are before I start doing too much UI work.

Today's Vocabulary:

Subclass. A class which inherits all of the properties and abilities of another class but can add more specific unique characteristics. Poodle is a subclass of Dog- a Poodle can do all the normal Dog things like Bark and Eat, but it can also Strut, which not all Dogs can do. You can subclass a subclass (like Canadian Attack Poodle could subclass Poodle) but you can generally not inherit from two different classes at the same time (like Poodle and Retriever).

Interface: A sort of contract that states that any class which implements the interface is guaranteed to have certain properties and abilities. For example, the IDrawable interface could stipulate that any implementing class must have a DrawOrder value and a Draw() method. However, the exact implementation of methods is left to the implementing class, so you could have a class whose Draw() method does absolutely nothing. In return for implementing an interface, a class can be used any time that interface is asked for, so if a certain bit of code requires an IDrawable object, I can give it anything that implements that interface regardless of what type of object it is. Classes can implement any number of interfaces, in stark contrast to the limit on class inheritances.

10 Comments

10 Comments

Avatar image for soulcake
soulcake

2874

Forum Posts

1

Wiki Points

0

Followers

Reviews: 0

User Lists: 0

Nice nice good job cant wait for this to be a bit playable :D

Avatar image for audiobusting
audioBusting

2581

Forum Posts

5644

Wiki Points

0

Followers

Reviews: 4

User Lists: 26

Edited By audioBusting

I feel like that's more inheritance than is ideal, but hey whatever works right? Keep up the good work!

Edit: I just noticed that this reads more condescending than I meant, sorry! I'm just trained to not favor inheritance unless necessary, but that solution is actually quite clever.

Avatar image for jimbo7676
Jimbo7676

881

Forum Posts

40

Wiki Points

0

Followers

Reviews: 1

User Lists: 0

Edited By Jimbo7676

Man I am stoked for this. It would be great if the community brings some awesome art assests. 1920x1080 is my resolution.

Avatar image for geraltitude
GERALTITUDE

5991

Forum Posts

8980

Wiki Points

0

Followers

Reviews: 17

User Lists: 2

So awesome - also very useful read for me, thanks duder.

Avatar image for peezmachine
PeezMachine

752

Forum Posts

63

Wiki Points

0

Followers

Reviews: 6

User Lists: 2

@audiobusting: There are certainly things I would change about this implementation if I were writing the engine from scratch (which I was inches away from doing when I first bumped into the GameComponentCollection problem). I'd probably have more interfaces in order to narrow down the inheritance tree - something XNA could have used, since my ideal solution unfortunately relied on me needing to inherit from two totally different XNA classes that had no comparable interfaces.

Avatar image for peezmachine
PeezMachine

752

Forum Posts

63

Wiki Points

0

Followers

Reviews: 6

User Lists: 2

@geraltitude: Glad to hear it! Hopefully some of these posts will help duders see that programming can get a bit hairy at times, but that the basics are pretty much just "poodles are a type of dog."

Avatar image for northsarge
NorthSarge

276

Forum Posts

979

Wiki Points

0

Followers

Reviews: 2

User Lists: 1

I just caught wind of this - and I love the idea. Keep up the great work, very excited to see this playable. Also my native res is 2560x1440.

Avatar image for tycobb
TyCobb

2036

Forum Posts

90

Wiki Points

0

Followers

Reviews: 1

User Lists: 0

Edited By TyCobb

Not sure what your base classes really look like, but if you wanted, you could also make them a contract like your interface. I am assuming you are in C# since it is XNA so you could make some of your base classes abstract and just add in placeholders for methods that the derived classes need to implement, but don't need any logic from your base (i.e. no need to call base.DoSomething(...) or implement empty virtual methods).

I wasn't sure if you were doing anything like that or knew what an abstract class was so I thought I would just mention it because it's a pretty great ability.

I am not sure if you realize or not, but you can inherit an infinite number of classes. They just have to be chained.

  • Poodle inherits Dog.
  • Dog inherits Canine.
  • Canine inherits Animal.
  • etc.

So my Poodle is all of these things. I know some do not like it this way, but it helps to make you think about what you are doing. There is no reason for Poodle to inherit Dog and Canine, but Dog not inherit canine. C# isn't the only language to have this restriction. It helps with polymorphism and I am not sure how you would even go about calling a base method if Dog and Canine were to share the same method and signature, but not inherit one another. Trust me the limitations are there for good reason and I am thankful for them because I do not want to see code where someone decided to make some insane super class. =)

I am very eager to see more of this. Keep up the good work.

Avatar image for peezmachine
PeezMachine

752

Forum Posts

63

Wiki Points

0

Followers

Reviews: 6

User Lists: 2

@tycobb: Indeed, I'm well versed in both Abstract Classes and proper class hierarchy! I definitely use a lot of Abstracts for base classes - for example, I've never going to create a PlayerCharacter straight up, it's always going to be a subclass like Drew or Vinny, so PlayerCharacter is abstract with any all-purpose methods I can use implemented and the others just exposed.

Avatar image for tycobb
TyCobb

2036

Forum Posts

90

Wiki Points

0

Followers

Reviews: 1

User Lists: 0

Edited By TyCobb

@peezmachine: Cool. I was just throwing it out since they are awesome and wasn't sure what if you knew or not.