Welcome guest. Before posting on our computer help forum, you must register. Click here it's easy and free.

Author Topic: Begining my first C# project, need pointers (no pun intended)  (Read 4990 times)

0 Members and 1 Guest are viewing this topic.

Linux711

    Topic Starter


    Mentor

    Thanked: 59
    • Yes
    • Programming Blog
  • Certifications: List
  • Computer: Specs
  • Experience: Experienced
  • OS: Windows 7
So I've finally decided to start working more in C# instead of VB.NET and VB6. I am starting my first major project and it's going good so far, but I need help with deciding how to structure the program. My idea is to eventually make an above view game (3rd person) like the old Command and Conquer series. To start I am going to use GDI+ graphics because the API seems much easier than DirectX or OpenTK which were inherently designed for 3D purposes. I am probably going to lower the screen resolution if it becomes too CPU intensive.

This question is mainly targeted towards BC_Programmer because of his BaseBlock game that I'm pretty sure also uses GDI+. So my questions are: How are the classes laid out? How do you update the OnPaint event? Is it double buffered? How do you make the GDI+ more efficient? Do you have a new class for each different type of object?

What I have right now:

The form:
Code: [Select]
namespace TankGame
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();

            Graphics dc = this.CreateGraphics();
            Game g = new Game(dc, this);
        }
    }
}

The game class:
Code: [Select]
namespace TankGame
{
    class Game
    {
        private Sprite tank;

        public Game(Graphics screen, Form frm)
        {
            frm.Paint += new PaintEventHandler(DrawScreen);
            LoadObjects(screen);
        }

        private void LoadObjects(Graphics screen)
        {
            tank = new Sprite("tank.bmp", 0, 0, screen);
        }

        private void DrawScreen(object sender, PaintEventArgs pea)
        {
            tank.Update();
        }
    }
}

The object:
Code: [Select]
namespace TankGame
{
    class Sprite
    {
        public Bitmap objImg;
        public int X;
        public int Y;
        private Graphics screen;

        public Sprite(string initName, int initX, int initY, Graphics s)
        {
            objImg = new Bitmap(initName);
            s.DrawImageUnscaled(objImg, initX, initY);
            X = initX;
            Y = initY;
            screen = s;
        }

        public void Update()
        {
            screen.DrawImageUnscaled(objImg, X, Y);
        }
    }
}
YouTube

"Genius is persistence, not brain power." - Me

"Insomnia is just a byproduct of, "It can't be done"" - LaVolpe

BC_Programmer


    Mastermind
  • Typing is no substitute for thinking.
  • Thanked: 1140
    • Yes
    • Yes
    • BC-Programming.com
  • Certifications: List
  • Computer: Specs
  • Experience: Beginner
  • OS: Windows 11
Re: Begining my first C# project, need pointers (no pun intended)
« Reply #1 on: June 07, 2013, 11:56:30 PM »
This question is mainly targeted towards BC_Programmer because of his BaseBlock game that I'm pretty sure also uses GDI+. So my questions are: How are the classes laid out? How do you update the OnPaint event? Is it double buffered? How do you make the GDI+ more efficient? Do you have a new class for each different type of object?

BASeBlock runs game logic on a separate thread, This separate thread runs the gameproc() routine, (which is ridiculously gigantic). Essentially, the thread constantly repeats, and basically does:

1. Update all Game Objects
2. force the display to refresh

The second step is performed with something like this:
Code: [Select]
PicGame.Invoke((MethodInvoker)(() =>
                            {
                                PicGame.Invalidate();
                                PicGame.Update();
                            }));

Since the GameProc() routine is running on a separate thread, it cannot access UI Controls without causing an exception. The code uses Invoke on a UI control (in this case PicGame) to run a piece of code on the UI thread itself. This logic invalidates the picturebox and updates it, which results in  the Paint Event of the Picturebox being fired. The Paint event is more complex, because for performance reasons Blocks that don't need to be redrawn aren't. The result is that there is a buffer for the background, one for blocks, and one for animated blocks. the block bitmap is only changed if a block has changed. Since most blocks are static and don't move or change very often, this is a rather big improvement speed-wise.

The GameProc thread sleeps for a set period of time every iteration, which is calculated based on how fast the FPS currently is. Sadly I forget most of the logic involved. It's not much different from the core idea behind the GDI+ Particle "tutorial" I posted to youtube. BASeBlock's source, though not 100% up to date, is available on my github. In order for the project to open,Build, you need VS2012 (not sure if it will open in express, either), as well as the BASeBlock resources installed (which are installed by the game installer in the desired location). You will also need BASS.NET and of course BASS itself for Sound support. (optionally, it also supports IrrKlang but I've not used that particular "driver' class in a long time)

As for how the classes are laid out...

Basically there are some base classes, Block, GameObject, cBall, and cParticle. Each Block in the game can trace it's inheritance tree to Block; for example, BrickBlock derives from TexturedBlock which derives from ImageBlock which derives from Block, EllipseBlock derives from PolygonBlock which derives from Block, etc. Each one naturally simply adds special behaviours to it's parent; ImageBlock makes a Block draw from an Image, TexturedBlock makes that Image a Texture, and BrickBlock uses Textures as well as a special destruction behaviour that spawns certain Particles/GameObjects, etc.

Powerups are implemented via interfaces, and the Paddle can have a set of PaddleBehaviours Applied to it. Over time I've added a lot of utility methods and classes to make certain things easier.

For your current implementation, I would probably make a few changes. The first would be to move your startup code into the Load Event handler for the form; the second would be to change the Game object to not accept a Form, but instead an implementation of an interface that your Form can implement. In BASeBlock, I used a interface called "iGameClient", which is implemented by the Form. The purpose of this is to provide the capability for changing what implements the interface, and prevent strong coupling with the Form itself.

One of the issues originally was preventing cross-thread calls, and most importantly preventing blocks from being removed while the collection was being enumerated, since that would throw an exception. Originally I had a very gross hack that basically passed by reference lists of objects to remove, but I changed that to allow for a collection of delegates to be called before the next frame "tick" occurs. Removing blocks, balls, GameObjects/etc are usually done within the logic for them, but now they defer actual removal until the beginning of the next frame's logic. Also because I was using multiple threads, I had the paint routine working with the game's objects and the "tick" routine as well, so I had to perform some locking to prevent the game "updates" from removing or adding elements (such as in the previous delegate listing) while the painting routine was iterating over them.


Slowdown: With logs of stuff on the screen, the game simply cannot necessarily process each tick as fast as would be ideal. As a result, I've implemented logic that prevents the entire game from slowing down; basically 30fps is the desired speed; if the FPS is higher than that, movement will be smoother but still travel the same distance in the same timescale. if the FPS is 15fps, then objects will move about twice as fast per frame to make up for it. There are of course some issues with this. I f ound heavy particle usage also caused performance issues so I cap the number of particles that can exist, using a Queue. Some particles are tagged as "critical" and won't be removed in this manner, but most particles are used for aesthetic purposes so most are able to be removed if this limit is reached.

Anyway, the most common approach to this I've seen seems to always employ a Timer of some sort, and use the Timer event to handle each tick. But the problem is that that tick logic is occuring in the UI thread and while it operates the UI is completely blocked, so it's almost always best to keep everything that isn't drawing related outside of the UI thread.





I was trying to dereference Null Pointers before it was cool.

Linux711

    Topic Starter


    Mentor

    Thanked: 59
    • Yes
    • Programming Blog
  • Certifications: List
  • Computer: Specs
  • Experience: Experienced
  • OS: Windows 7
Re: Begining my first C# project, need pointers (no pun intended)
« Reply #2 on: June 26, 2013, 11:28:31 PM »
Thanks for all the info. I watched your video and looked at the diagram. I've kind of put this project on hold until I get more used to programming in C#. I have other smaller projects that I am working on now, so this has gotten pushed to the end of my list.
YouTube

"Genius is persistence, not brain power." - Me

"Insomnia is just a byproduct of, "It can't be done"" - LaVolpe

BC_Programmer


    Mastermind
  • Typing is no substitute for thinking.
  • Thanked: 1140
    • Yes
    • Yes
    • BC-Programming.com
  • Certifications: List
  • Computer: Specs
  • Experience: Beginner
  • OS: Windows 11
Re: Begining my first C# project, need pointers (no pun intended)
« Reply #3 on: June 27, 2013, 02:37:33 AM »
I've kind of put this project on hold until I get more used to programming in C#.

Sounds reasonable. Technically my first C# Application was a Hijackthis/DDS style tool clone.
I was trying to dereference Null Pointers before it was cool.