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 » January 12th, 2020, 9:05 am

19 days to go, yikes.

There are a few things I'm running into I didn't think about ahead of time.

Forward declarations only work for outermost classes/structs/etc... They don't work on nested types. In other words, I can't forward declare: enum class Ammo::Owner, and as far as I can tell I can't even use it as a return type which you normally can for forward declared types.

The reason I went on this MVC journey was to practice a design pattern that helps with decoupling. Now in some instances I am having to #include the Model headers in the Controller headers. My only option forward I'm seeing is to make a header file with enums and constants that I would have normally kept in classes because the header for some controllers need them as well as the Models. This is probably the quickest and easiest way to decouple at this point, but dammit I've already done that and changed it back after a few alterations ( sobs ).

So if/when I try this again, I'll be making a few changes.

1) Model is data only. Currently I have Update functions in each entity model, and it's just to update timers and positions or anything that doesn't require branching with the exception of the state itself, though I could have models for each state do avoid branching altogether.

2) Controller is core logic only. This is already how I'm doing things, all functions such as getters and setters are listed in the Controller for each object. This has made WorldController functions very verbose.

3) View is render logic only. While I mostly adhere to this now, my GameView instantiated the Graphics class and I don't feel like changing it. I did have sprites in the entity views, but different objects are going to need different sprites. In order to alleviate this, I just made my sprites static for each entity and make the models "store" the sprites. I'm trying to avoid making a View for each Enemy# and Boss# at this point. I got around this by creating 10 overloads of the BossView::Draw function and just use the std::visit function to pass on the variant object.

I'm almost done with the logic I think. There are a few compilation errors ( 30+ ) that I need to fix then it's on to filling in the Boss3-10 update structures.
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 14th, 2020, 4:31 am

Went without internet for over 24 hours and got REEEEEALLY bored. So I tried programming the whole time...not a good idea. After my brain became mush, I started mindlessly altering the entire chili framework to fit into the MVC design pattern, sigh.

Something of note about how I ended up interpreting the MVC pattern...IT'S FUCKING C or more precisely, it's procedural programming. Sure I have some benefits of C++ the language, like templates, static functions and namespaces, which also means I get the C++ template library so things like std::variant, std::array and std::vector. Quite literally though, taken a bit further by removing all the C++ features this could easily be done in C.

Models are nothing but structures of data...C struct
Controllers are the list of functions that control the logic, in C++ I can do

Code: Select all

// Declare like so:
class GameController
{
     static void Update( Game& model, float dt )noexcept;
};

// Call like so:
GameController::Update( game, delta_time );
In C:

Code: Select all

// Declare like so:
void GameController_Update( Game* game, float dt );

// Call like so:
GameController_Update( game, delta_time );
The only reason it ended up this way is because I didn't want to instantiate a controller and view for each instance of the same type of model. As mentioned before, I didn't want to instantiate 250 StarControllers and 250 StarViews for the 250 Star models. The bookkeeping for instantiating and removing associated EnemyControllers and EnemyViews was a pain in the ass. So, now I get C style programming where I don't instantiate any controllers or views and I have to pass in the model to each to have work done on them. Encapsulation without OOP, what a life.
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

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: What to do?

Post by chili » January 14th, 2020, 12:41 pm

I've been cockblocked by the inability to forward declare inner classes in the past. It's another indication that I need to curb my inner class fetish.
Chili

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

Re: What to do?

Post by albinopapa » January 14th, 2020, 2:20 pm

@chili Haha, inner classes seem so clean sometimes though. Unfortunately, it's not just inner classes. It's enums and using aliases as well. I've come to realize during this project that I will have to consider that if the outside world needs this stuff, they should all be declared/defined outside the class. This kind of sucks. In the case of the enums, I could write Asteroid::DeathReason::AffectedByHero if I'm willing to #include "Asteroid.h", otherwise I'm left with AsteroidDeathReason::AffectedByHero. I could have written Hero::Controller::Update, but nope I have to write HeroController::Update. With the former, templates would be simple, T::Controller::Update but now I write EntityController<T>::Update.

Hehe, I've thought about doing something like this next time:

Code: Select all

template<typename T>
class Entity
{
public:
     Entity(Vec3f const& pos, Vec3f const& orient, std::shared_ptr<Mesh> mesh, std::shared_ptr<Texture> tex)
          :
     view( this ),
     controller( this ),
     model( pos, orient, std::move( mesh ), std::move( tex ) )
     {}

public:
     class Model
     {
     public:
          Model( Vec3f const& pos, Vec3f const& orient, std::shared_ptr<Mesh> mesh, std::shared_ptr<Texture> tex )noexcept;
     private:
          Vec3f position, orientation, acceleration, velocity;
          std::shared_ptr<Mesh> mesh;
          std::shared_ptr<Texture> texture;
     };
     class View
     {
     public:
          View( Entity const* entity )noexcept;
          void Render( Pipeline& pipeline )const noexcept
          {
               pipeline.SetVertexBuffer( mesh.controller.GetBuffer() );
               const auto transform = 
                    Mat4::Rotation( parent->controller.GetOrientation() ) *
                    Mat4::Scale( 1.f, 1.f, 1.f ) * 
                    Mat4::Translation( parent->controller.GetPosition() );
               pipeline.SetConstantBuffer<VertexShader>( transform );
               pipeline.SetTexture( parent->controller.GetTexture() );
               pipeline.Draw();
          }

     private:
          Entity const* parent;
     };
     class Controller
     {
     public:
          Controller( Entity* entity )noexcept;
          void Update( float dt )
          {
               parent->velocity += parent->acceleration;
               parent->position += ( parent->velocity * dt );
               parent->acceleration = { 0.f, 0.f, 0.f };
          }
          Vec3f const& GetPosition()const noexcept;
     
     private:
          Entity* parent;
     };

private:
     Model model;
     View view;
     Controller controller;
};

Code: Select all

// Declare tags
struct Hero{};
struct Enemy1{};
struct Enemy2{};

// Declare variant
using Enemy = std::variant<Enemy1, Enemy2>;

// Declare entity
Entity<Hero> hero;
std::vector<Entity<Enemy>> enemies;

// Usage

void App::Controller::Update( float dt )
{
     hero.controller.Update( dt );

     for( auto& enemy : enemies )
          enemy.controller.Update( hero.controller.GetPos(), dt );
}

void App::View::Render()const
{
     hero.view.Render( pipeline );

     for( auto const& enemy : enemies )
          enemy.view.Render( pipeline )
}
I think that would defeat the purpose of the design pattern though. This design in particular will have it's own drawbacks as well anyway.

Now that I look at it though, I could have done something like this with my current design. Not necessarily nested classes, but making a super class that instantiates and holds it's own model,view and controller.
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 17th, 2020, 4:10 am

One of the most confusing things to me right now is the View. My original understanding of the View portion of Model View Controller was the visual representation of an object, the graphics portion. However, look at std::string_view. It has nothing to do with the visual representation of a string, but just a constant container that allows you to look, but not touch. So now I'm wondering if I got this all wrong, should all my getter accessors be defined in the views of each object and treat the View as a "look, but don't touch" type of view or should I maintain it to mean a visual representation of the object?

The C++ standard library isn't the only place where a "view" isn't necessarily the visual representation of an object. Direct3D has TextureViews and RenderTargetViews which are not the Texture and RenderTargets you supplied to the pipeline. They are, however, the way you interact with those objects or at least how the pipeline interacts with those objects. They are the "you can look, but not touch" mechanisms for the Models "ID3D11Texture2D" and "ID3D11RenderTarget".

I'm still almost inclined to think the original idea behind the MVC design pattern actually meant graphical representation since it's mostly used and referred to when talking about webpage design and interface as well as MVVM ( Model, View, ViewModel ) which is similar, but designed for UI.

All that being said, I think I'm almost done with porting the chili framework to MVC, though the sound stuff is way over my head and I may have broken something..can't test yet.

I have done away with the Graphics class and moved some of it into the Window, WindowController and WindowView classes. The rest I decided to make the Surface classes a sort of render target that is passed around to each of the Views for rendering to. Each Model has an Effect that the corresponding View uses to render to the Surface as a render target. The WindowView will be passed this Surface to be presented to the screen.

One thing that I've noticed about my decision to make all the members of the Controllers and Views static is I can't really keep non-static data in them. The only reason this is an issue is that I have to keep graphics data in the Model as that's the only thing that actually stores data. That should be okay since some portions are shared between multiple systems anyway, like phsyics needs position or at the very least a bounding volume that has been transformed to world coordinates and so does the graphics, so yeah just reassuring myself there. Of course, this could be why some people say MVC isn't a good fit for games.

Less than 15 days to go, I really have some work to do.
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 19th, 2020, 8:59 am

Woo-hoo!! I have finished converting the framework to use the MVC pattern, all the compilation errors are gone and it...runs-ish.

It runs, but I am in the process of making some changes so nothing really happens other than the player and the shield. The projectiles still fire and I can change weapons, but no asteroids nor enemies spawn.

The changes I've made so far aside from the MVC porting are I am going to have the enemy spawner now handle the positioning of enemies and their paths, no more static arrays for each enemy type. I'm hoping I'll get some sort of formation flying out of this.

I can't recall if I mentioned this before, but I made a Grid class for storing pointers to objects in the "world". This way, only objects within the same Cell get a collision test. The object's bounding rectangle is used to determine the Cell, so if it straddles two cells it get's added to both cells. This will certainly bite me in the ass if two objects straddle the same two cells...I didn't take that into account when I did this, but I'll have to figure out a way to handle it. I suppose the easiest way would be to keep track of each object collided with and reject further collisions that frame from the same entities.

I moved all Model updates to the controllers, so now the models are just private data with the controllers and views as friends, sometimes more friends are added. Models still retain their constructors mostly, a couple classes like the Surface class lost their constructor privileges, others may follow in favor of factory functions in the Controller classes.

I moved the Effect objects out of the entity types and made them static members of the Views for those types. I didn't know how the Draw functions fit in ( do they go to Controllers? Views? ) because if I put them in the Controllers I'd have to make each entity have their own Effect. Since the data changes I was able to just put a single static instance in their Views and just update the static effect object with their sprites and other data.
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 19th, 2020, 9:09 am

EnemyBehavior branch

I don't know why this ended up on my EnemyBehavior branch when I made an MVC branch, but here's the current code thus far.

As I said, nothing real exciting happening, but at least it's running...on my PC anyway.

I'm not sure it will run on anything under Windows 10, or at the very least nothing below C++17. I switched out a lot of ChiliException code with std::system_error exceptions so I don't know how that works on Windows 7,8,8.1.
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 20th, 2020, 10:13 am

11 days to go and I'm pretty sure I'm not going to make the deadline, even for the base mechanics.

I finally started working on the Enemy# classes' behavior. Aside from architecture, design is another downfall of mine. I feel I'm not very creative, but I'm having fun and haven't burnt out yet so hopefully it's a good sign. I get ideas popping in my head for things I'd like to see, however, looking at how I have my current framework, there are going to have to be quite a few changes to enemy spawning and maybe even their logic.

In the original version, the enemies just blindly followed a path of way points. The spawning was all based on time delays ( every 3 seconds spawn a new group of max enemies and every .5 seconds during that spawn cycle spawn a new enemy ). I'd like to maybe have more flexibility with how they spawn.

The Enemy1 group will spawn as a group all at once. When they spot you through engaging, they run away triggering the next group to spawn.
The Enemy2 group will probably be small maneuverable short range fighters that might follow a path and fire projectiles at the player before returning back to base.
The Enemy3 group will probably come in from the top, leave the bottom, then go back to the top and as they slowly fly by on their way back to base, they will be firing sideways at you for a few seconds.
The Enemy4 and Enemy5 groups I haven't thought of yet.

Level1 boss basically just sits there firing missiles at you
Level2 boss will lock on to you and try ramming you. Once it reaches your position ( or where you were when it started ramming ) it has to stop for a cool down period.
Level3-Level9 bosses I haven't fully fleshed out
Level10 I want to have a massive ship with turrets on either side. This ship will come from the top, surrounding you with 3 turrets on either side. Destroy the turrets and the command center and you win.

Level4 I'm hoping will be a little different. You will have to navigate through an asteroid belt, so there will be way more asteroids to dodge.

I'm slowly coming up with a story that is helping me with some of these ideas. Nothing written down ( except here ), just making it up as I go.

I thought about having a camera and giving the game a map or sorts. Not a visual map, but a layout of the trek the hero takes from his/her home base to the enemy's home base. I ditched this idea for now in favor of just simulating it using scrolling stars/backgrounds, levels and transitions. The transitions will come much later, they won't be part of the main architecture/game until I have all the level designs done and all the bugs worked out.

I still haven't even worked in how the player will get their weapon upgrades either. Right now, you start with a single shot, rapid shot and plasma guns. This might be something that is either mission based or something the user can acquire between levels or something you get throughout the level as powerups.

That's it for now, I have to redesign the enemy classes as well as come up with their behaviors.
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 27th, 2020, 8:23 am

Seems like I may have fallen a bit behind. The past week I haven't really touched the project due to fatigue. After a while, things start to run together and I don't know what I'm doing. So I am taking a break. I'll miss my self appointed deadline, but what can you do in situations like this really?

At least I wrote down in the other post some ideas, that should help once I get back on it.

Lately, I've been allowing myself to get distracted with a game called "Antimatter Dimensions". It's one of those progression games, I'm not going to sell you on it if you aren't already playing it. It's a huge time-sink. The only thing that made it fun was competing with my brother on who's upgrade path would be faster. So far, his have been, I am only ahead because I have spent more time actively "playing".

Anyway, now that I've bought all the upgrades that basically play the game for me, I'll be able to get back on task in a day or two, okay it will definitely be three or four. I'm hoping I'm only about a week behind. I just need to write up the code for the enemies and bosses, then I'll be able to test.
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