What to do?

The Partridge Family were neither partridges nor a family. Discuss.
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » December 19th, 2019, 11:05 am

Hey, thanks for the reply.

I understand what all the patterns are trying to accomplish ( decoupling ), so maybe you're right about finding something that fits me I suppose. The couple of times I did attempt implementing the MVC pattern, one thing I noticed was I felt less restricted knowing that all the graphics related stuff was separate. I felt like I had more freedom to work on a single portion of logic. Words are failing me right now. I think I will go back and try using my own interpretation of the MVC pattern on this project and see how far I get. For one, I want to change to using transformable sprites and I have some things to change that hopefully make it easier to make the graphics changes.

One thing that I think I might get hung up on is how state changes are going to be handled. I guess the first thing I need to decide is the roles each structure will have; are Models going to be the data or the simulation step, if Models just store data then the Controllers can handle the simulation step and state changes ( basically a bridge between View and Model ). In one description, the View would inform the Model about inputs, but I think the Controllers will handle input, so when the user presses Esc during gameplay, the GameController would change the View from PlayView to PauseMenuView.

Thanks for trying the game out, glad you liked what you seen so far. I only got to play 1943 a few times when we'd go visit my aunt who lived 7 hours away, so not very often. I did play a game called Air Fortress on the NES. It was a two parter. The first part was a side scrolling space shooter and then you land on an air fortress. From there, it was a platformer of sorts, but you could fly instead of having to jump everywhere. That one would be kind of fun to clone.

Anyway thanks again and I'll definitely be posting here as often as the project allows.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » December 21st, 2019, 8:20 pm

One of the reasons I like using structs over classes is being able to use braced initialization without having to make constructors.

Code: Select all

struct Object_struct
{
   Vec2 position;
   Vec2 direction;
   float speed = 240.f;
};
class Object_class
{
public:
   Object_class()=default;
   Object_class( Vec2 const& pos, Vec2 const& dir, float spd = 240.f )noexcept
      :
   position( pos ),
   direction( dir ),
   speed( spd )
   {}

private:
   Vec2 position;
   Vec2 direction;
   float speed = 240.f;
};
So much more boilerplate for the class version. With the second constructor, the creation of either is the same:

Code: Select all

auto obj_struct = Object_struct{ Vec2{ 400.f, 300.f }, Vec2{ 0.f, -1.f }, 240.f };
// or
auto obj_class = Object_class{ Vec2{ 400.f, 300.f }, Vec2{ 0.f, -1.f }, 240.f };
So why the need for the constructor( s ) for classes?
Well, if everything were public in a class there wouldn't be a need and for all intent and purpose it would be the same as a struct. However, because a class should be used to maintain it's invariance, members should be private. Braced initialization or more technically Aggregate Initialization can only be used for public members and on objects without constructors, follow link for all rules.

So why use classes at all? As mentioned, classes should be used to maintain invariance. For all of us that have no clue what this means, I'll try explaining it the best I can.

Put simply, it's a way of keeping the data members within the realm of sanity. For instance, it you had a Person struct with a string for the date of birth, you'd have no way of making sure that the string literally held a possible date of birth: December 25, 0001.

However, with the use of member functions and keeping that string private, you can maintain a string that represents a day.

Code: Select all

void SetDateOfBirth( enum class month, uint8_t day, int16_t year )
{
    std::stringstream ss;
    switch( month ) 
    {
        case Month::January:
            ss << "January"
            if( day > 31 ){ throw std::invalid_argument{} }
            else ss << ' ' << day;
        // finish for all months
    }

    ss << ', '<< year;
    dob = ss.str();
}
This is more than encapsulation or why encapsulation is so important. When I read or hear people discuss encapsulation it's usually in reference to making sure values cannot be changed without knowledge by the owning class. Somewhere in your code you may be expecting the value of a member to be one thing, but it got changed to something else in another part of your code. Another point is implementation details that code elsewhere doesn't need to know about or shouldn't even dare touch.

Using encapsulation helps maintain invariance by only allowing things that can be changed to be modified within limits or reason and preventing changes to members that should not be changed.

Aside from the technical aspects of encapsulation, it also allows code to be more straight forward. Say you have an entity that when equipped with a power-up health can be twice it's max. If you just use public members then you'd be doing the checks in the main logic of the game:

Code: Select all

if( entity.Bounds().OverlapsWith( health_pack.Bounds() )
{
    entity.health += health_pack.value;
    if( !entity.has_extra_health_powerup )
        if( entity.health > entity.health_max )
            entity.health = entity.health_max;
}
On the other hand, if you stick to encapsulation allowing the Entity to maintain it's invariance:

Code: Select all

if( entity.Bounds().OverlapsWith( health_pack.Bounds() )
    entity.AcquirePowerup( health_pack );
Having said all that and getting back to my opening statement, my current project uses structs for aggregate initialization to be possible as well as defining member functions to make the outside code clean. However, I know that if I were to be working on this project with a group of people, I would probably choose to go with classes to maintain encapsulation and therefore maintaining each objects' invariance.

Honestly, I enjoy the simplicity of being able to define a struct without constructors and being able to use aggregate initialization. It kind of sucks that there is a restriction about private members, but I suppose you can't maintain invariance otherwise.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » December 21st, 2019, 8:28 pm

On the other hand, if you stick to encapsulation allowing the Entity to maintain it's invariance:
I really didn't finish that thought, but I meant to say, allowing the Entity to maintain it's invariance through encapsulation the game logic section of code is much cleaner and easier to maintain. Having errors in a function with a few lines of code vs a few hundred lines of code is easier to debug. This also keeps error checking local to the error instead of somewhere in the large wall of code in the middle of the game logic.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » December 31st, 2019, 8:55 pm

The MVC rewrite has been taking a bit longer than expected. The holidays are almost over so I'll be able to commit more time in about a week.

I struggled in the beginning over something that looking back upon shouldn't have tripped me up. I figured I'd share my design so far and see if anyone has any thoughts on it.

Model: Best described as the data layer, but sometimes referred to as the simulation logic. So this is where all the data resides. Each object with a few exceptions has a Model. Hero, Enemy, Ammo, Boss, World, Game, etc.. ). Each model is responsible for updating itself in it's current state. Models don't know anything about how to change their state unless you consider updating their positions a change in state, I consider a change in state something like changing directions based on external causes, ( player presses UP on the keyboard or enemy reaches a way point and has to switch to the next which changes it's direction ).

View: Displays what needs displaying, has only enough data to complete it's task, gets it's data from the Controller. Not all objects need a view. The spawners for instance won't need a view nor will the weapons since they aren't displayed. Perhaps later weapons will get a view, but I doubt it for this game as my goal for this is to keep it pretty simple.

Controller: I'm pretty sure everything in my game will need a controller as this is going to be the "brains" for everything. Anything that needs a state change is done through a controller such as determining direction, determining if a weapon can fire, changing screens to/from menus, etc.

So, the thing I was stumbling on was I have the stars in the background. There's 250 of them and I was trying to find a way to avoid 250 models, 250 view, 250 controllers. There are several ways I could have done this, but I chose to go that route anyway just to keep with the MVC design pattern. I have a WorldController which has 250 StarControllers. Each StarController updates their own Star's state. The only state a StarController needs to worry about is when it leaves the bottom of the screen, it gets it's Y position reset to 0 and it's X position randomly generated. Each Star is a model and thus only updates it's position downward. After updating the position, each StarController forwards the position of the star to the StarController's StarView.

The flow will probably look like this:

Code: Select all

// In the WinMain function
{
	auto game_model = Game{};
	auto game_view = GameView{MainWindow& wnd};
	auto game_controller = GameController( model, view, MainWindow& wnd );

	while( wnd.ProcessMessage() )
	{
		game_controller.Update();
		game_view.Draw();
	}
}

GameController::Update()
{
	const auto dt = timer.Mark();
	// Handle user input such as Escape changing to Menu if already playing
	// or handling menu inputs etc.

	case Game::State::Playing:
		// Call WorldController::Update to update world controller, model and view.
		world.Update( kbd, dt );
        
		// Check if hero is dead, change game.state to Game::State::Gameover
		// Check if level is complete, change game.state to load level ( future update )
		// Check if defeated last boss, change game.state to congrats screen ( future update )
		break;
}
I'll need the MainWindow for the GameView to initialize Graphics, but I'll also need the MainWindow in GameController as well for the Keyboard as well as calling MainWindow::Kill to end the program. The GameView will interact with the Graphics class ( it's like a WindowView ), the GameController will interact with the MainWindow more directly so I will consider this the WindowController, but since it holds the Mouse and Keyboard, I suppose it will also play the part of Model, unless I plan on going the extra mile and make a Mouse and Keyboard Models and Controllers.

We'll see how this takes shape. I hadn't thought that far ahead yet.

Hopefully I have explained my design enough. I'll post the code to github once I get it back in a playable state.

Aside from that, I've laid the ground work for being able to rotate sprites. I used chili's ideas from 3D fundamentals about a template based effects pipeline. The nice thing about how "restrictive" 2D is, is I'm only drawing quads, so I don't really have to worry about a vertex buffer or vertex constant buffers. I still have the simple DrawRectangle, DrawSprtie and so on for an interface, but all the pipeline stuff is handled inside those functions.

I was able to figure out a way to handle drawing the Shield in it's different color states by allowing a color tint to be passed to the DrawSprite function. This tint color is passed to the PixelShader's constant buffer and multiplied by each pixel in the sprite. By default, the tint is white, but for the shield it will be green, yellow or red depending on how much health is left.

Pretty much all the objects will be represented as sprites. Some will be loaded from file, others will be generated at startup. This means I only really need one pipeline effect, but I have others in case I want something different.

I'll keep working on it and updating this thread. I'm still hoping to have something more polished by the end of January, which was my deadline I gave myself to finish this thing. Just to be clear, hehe, the deadline is for programming not art or sounds. I'm perfectly fine with using placeholders while I get this coded since that's the main issue I have been having and the purpose for this project.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 2nd, 2020, 9:54 am

Almost there.

One should not code while half asleep, things get sloppy.

I have 93 errors to clean up assuming VS didn't just call it quits at that point.
I am not entirely sure I like what I've done. There were a couple of challenges based on some decisions I made regarding how controllers are constructed, but I seem to have gotten around most if not all of them. There's some code duplication between the model and the view because I have the controllers coordinating the two so it couldn't be helped. For instance, Game::State states had to be duplicated in GameView as GameView::State. I can avoid this by making the enums in their own file and giving a full name of GameState, WeaponState, etc..., then they can be shared between the models and views.

Other things shared are the positions or more precisely the bounding rectangles in most cases. Once the model has been updated, the controller sends the updated rectangle to the view. In this way, the controllers update functions are called separately from the views' draw function. The way I have it now, the models' update function is called inside the controllers' update function. So normally, I try to make it so that the state/behavior is updated then the model's simulation step ( no if/else blocks in any of the Model::Update functions ). This wasn't always possible since the behavior changes based on the model's sim step like updating a counter first before checking if it needs to trigger some behavior change like in the case of the Weapons, so that was rather annoying.

As of yet, I haven't had a chance to compile and test out any of the new graphics pipeline stuffs. I just finished converting everything over to the MVC architecture and then these 93 errors pop up. I'm almost positive most are going to be forgotten #include statements, that's going to be a joy. Looks like I'll be posting the updated code soon.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 3rd, 2020, 5:09 am

Fixed all the compilation errors. As expected most were caused by missing #include statements.

The menus are working, but the game play is not. I was hoping I'd get something for free from all this, but that is sadly not the case. I was hoping that the Views would be initialized with data upon creation or at the very least upon update, but only a few things did.

I'm getting a runtime error that I haven't tracked down yet. I think it has something to do with invalid references. All my controllers are instantiated with references to the corresponding model and view. In the case of projectiles, enemies and asteroids I have to instantiate a model and a view in order to instantiate the controller. Well, I didn't take this into account when deleting the models...whoops. So the controllers had invalid references to their respective models, that's what I'm thinking. I haven't finished correcting this issue as of this writing. I'm going to call it quits for today.

I have switched everything over to class instead of struct, with the exception of the true data only classes like the Enemy# structures, which are just a collection of properties that don't change during runtime. There are a few classes that have proper encapsulation ( public, private sections ) and in doing so, a few of the controllers now have taken on public methods that would have normally been assigned to the models...GetPosition() or GetRect() for instance.

I'm also having to come up with ways of interacting with the View components. Since I'm delaying the drawing until all the controllers and models have been updated, I'm having to store some state in the view classes. Not a huge deal, but there's a lot of book keeping because of it. I'm considering a bit of a change on that part in order to relieve the controllers of their View duties. Plus, the Views won't have to store any members if I just pass a constant reference of the model to the view's draw function. I'm hoping this will alleviate some of the issues I've been experiencing and make it easier to continue. The reason I avoided this in the beginning is the coupling between the View and Model that would happen.

In some implementations of the MVC pattern, the model when updated would send a "Look at me" message to the View and the View can fetch the new data directly from the model. This would still mean storing some state in View since the rendering isn't done immediately. At the very least, I would cache the message until the draw call and then check the message to get the new data. However, since this isn't a static UI that only get's updated when the user presses a button, I think just passing the Model to the View::Draw function and getting the data directly will be better.

This might also help the dangling reference issue also. If the views aren't needed by the controller anymore, then I won't have to instantiate a new view for each controller. I can have one view for each type and the view can just use the model I pass in...I could probably do the same for the controllers as well. Most of the controllers don't store any state other than a view and a model. The WorldController however stores vectors of controllers for projectiles, enemies and asteroids. The HeroController stores a ShieldController and a WeaponController, so I'll have to figure out how to work that in as well.

Well, back at it again tomorrow afternoon or evening actually. Getting new computer parts as well as switching internet providers. For some reason my internet goes out every morning around 4:10am for about 10 minutes. I asked if they were rebooting their network every morning and they said; "No, this shouldn't be happening." Even tried replacing my modem/router a few times.

Anyway ttfn.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 3rd, 2020, 10:35 am

Shoot'n'Scoot MVC design


Okay, I made the changes I brought up in the previous post. Taking the responsibility of updating the view from the controller was a damn good decision I think. The code is way more stream lined compared to what it was, not that you'll ever know the mess it was in before that.

There's still some things to work out.
The shield color is wrong.
The shield changes color in the reverse order and flickers upon getting hit
The fonts are drawn a little high.
I am not sure if the enemy spawner is working correctly, I didn't see any of them spawn so perhaps not.
I only ever see one asteroid spawn, so asteroid spawner may not be working properly either.
The plasma ball sprite wasn't created properly so it looks funky.

Once I get those issues fixed I'll be able to see just how flexible or inflexible my interpretation of the MVC pattern is.

The purpose of posting a broken version is the code is mostly going to stay in this format, so I just wanted to at least see what anyone thinks of the interpretation.

I might already see how something like this would make it easy to say switch out the chili framework graphics for something a little more advanced. I'll have to resist the urge and stay focused on finishing the game play.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 3rd, 2020, 8:22 pm

Shield color: FIXED
Fonts rendering: FIXED
Enemy spawner: FIXED
Astroid spawner: FIXED
Plasma ball sprite: FIXED

I guess it's time to start coming up with new enemies and bosses.

I think I want more waves of enemies as well as have them spawn more frequently. Right now the pacing is very slow.

I also need to come up with a story, something that gives context and motivation for the player. This might help with some of the other aspects as well such as ship types and maybe some extra visuals.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 7th, 2020, 10:40 pm

Currently adding the level 2 boss.
Got sprite rotations working
Enemies now rotate to face the direction they are heading.

Tried using bezier curves to smooth out the paths of the enemy ships...I don't like the effect, so I'm going to have to come up with new paths.

While searching the net last night for inspiration for ship sprites, I came across a thread on a forum where someone used perlin noise for creating their asteroids...this is definitely going to be a thing.

They also made a modified tree generating algorithm to create their ships, I don't know if I want to go this route or not, but I do need to make some ship variety. I'm also considering making different sized ships, having the boss ships the same size as every other ship just seems wrong since they are going to be harder to kill and all.

I only have 24 more days left for my self appointed deadline...I don't think sound will make it in unless I change my thought process. Jan 31 could just be a milestone for getting the gameplay done then decide on another milestone for getting the game polished like sounds and visual effects as well as optimizations.

I have been trying really hard not to worry about performance. I know right now it runs smoothly in Release mode and until it doesn't I'm going to keep telling myself it's good.

For the effects I want to create a particle generator for the explosions. I also want to implement the bloom visual effect for the stars, explosions and laser beams, also anything else that comes to mind. I'll have to look back through chili's SSE tutorials for the episode on that.

After the rewrite I seem to have broken the shield from disappearing when depleted and the level 1 boss fires all 10 missiles at once and never again. Those are the last two "bugs" that I know of caused by the rewrite.

I originally came to post how my experience with the MVC pattern has gone, so here it is.

I noticed that the Controllers don't have to hold any data since deciding to pass in the Model to the update functions, so now all Controller functions are static. If I extend the same courtesy to the Views, I end up putting the sprites in the Model. I'm not sure how I feel about this really, but I have some ideas...SpriteM, SpriteV, SpriteC hehe.

I'm also seeing things a little differently about what the Controllers can do. Before, I had everything as structs and everything is easy to gain access to. However, I end up with things like:

Code: Select all

if( game_model.world_model.hero_model.health < 0.f ) 
     game_model.state = Game::State::GameOver;
I can just ask the WorldController if the hero is dead:

Code: Select all

if( WorldController::IsHeroDead( game_model.world_model ) ) 
     game_model.state = Game::State::GameOver;
Not saving much typing, but it's a lot clearer what the code is asking for. Anyway, now I can make my Models true classes and just have them as friends of their respective Controllers. The Models get their encapsulation from other Models and even other Controllers. I'm not sure if the Views should be friends or if they should go through the Controllers. I'm tempted to make them go through Controllers since so many of the Models are hidden behind facade classes that store variants for the main model classes. This would allow the variant visitor code to stay all within the Controllers and the Models keep encapsulation even from the Views. This should keep the Views very clean.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: What to do?

Post by albinopapa » January 9th, 2020, 5:00 am

With a time crunch feeling I decided to take an easy option.

I've created Level# classes with some "properties" such as enemy and asteroid spawn rates, counts and cycles. Realizing the simplicity of the game itself, this became a no brainer after I started implementing it. This will allow me to have my 10 levels that I wanted and only have to configure some properties...now I'm kind of wishing I WOULD have gone with a data driven design. Once I get the core logic in place I should be able to switch it up so I can play with the properties and not have to recompile, but instead just restart the game.

I've created a variable called cycles in each of the Level# classes to allow for multiple waves of the same types of ships, though I'll probably want an array of cycles...one for each ship type.

I created a grid class to help with collision detection. I plan on adding a lot more elements to the world and I'd like to try reducing the number of comparisons.

I am using std::variant quite a bit through everything in this game. It is cumbersome at times, but not too bad. I could have used inheritance on all of the entities ( asteroids, projectiles, enemies and hero ), but you lose type information doing that and being able to have the compiler/intellisense tell you that your fucking up before compiling or running is nice.

The cells in the grid are going to store pointers to the game entities. This makes the collision detection and handling way cleaner. Before I had to compare each type T to type U where it made sense manually ( Asteroid vs Asteroid but not Enemy vs Enemy ). Now, with all the entities sharing a cell being in the same vector, I just do a naive nested loop. The collision handler ignores collisions that don't make sense.

With template metaprogramming, I can get inheritance like behavior by creating a few type traits and/or overloads and specializations. I've thinking of going this route with the controllers for sure. I haven't thought about doing it to the views, mostly because the controllers interact with one another and I since I don't know the type of something I'll need to rely on the compiler. So instead of having a controller type for each Model, it'll just be specializations of EntityController<T> as most of the controllers seem to have the same interface anyway. It's that or using specialized type trait templates that return a specific controller: controller_t<Asteroid> would return an AsteroidController for instance. So in the case of a function template taking a T and U, I'd just call:

Code: Select all

if( controller_t<T>::AABB( entityA ).Overlaps( controller_t<U>::AABB( entityB ) )
{
     controller_t<T>::TakeDamage( entityA, controller_t<U>::Damage( entityB ) );
     controller_t<U>::TakeDamage( entityB, controller_t<T>::Damage( entityA ) );
}
If I wanted to I suppose I could instantiate the two controllers so as not to have to type out the whole thing.

Code: Select all

auto const controllerA = controller_t<T>{};
auto const controllerB = controller_t<U>{};
if( controllerA.AABB( entityA ).Overlaps( controllerB.AABB( entityB ) )
{
     controllerA.TakeDamage( entityA, controllerB.Damage( entityB ) );
     controllerB.TakeDamage( entityB, controllerA.Damage( entityA ) );
}
  • Adds more typing since now I have to write code to instantiate the objects,
  • objects have to be instantiated
  • now the rest of the code relies on those instantiations.
I like where this is heading, but I'm leery of how things will need to come together to get switching levels and all to work. Either way, I think I now have given myself a path that allows me to reach my milestone by Jan 31. Plans after that and a time frame will be decided after that.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply