This is the C++ source code for the "Arkanoid" demo. It was compiled by GCC and then converted into C# code by Mips2cs.


/*
 * FILE:
 *   bouncy_ball_game.cpp
 *
 * PURPOSE:
 *   example game using coercri + xna
 *
 * AUTHOR:
 *   Stephen Thompson <stephen@solarflare.org.uk>
 *
 * CREATED:
 *   23-Jun-2011
 *   
 * COPYRIGHT:
 *   Copyright (C) Stephen Thompson, 2011.
 *
 *   This file is part of Mips2cs. Mips2cs is distributed under the terms
 *   of the Boost Software License, Version 1.0, the text of which
 *   appears below.
 *
 *   Boost Software License - Version 1.0 - August 17th, 2003
 *   
 *   Permission is hereby granted, free of charge, to any person or organization
 *   obtaining a copy of the software and accompanying documentation covered by
 *   this license (the "Software") to use, reproduce, display, distribute,
 *   execute, and transmit the Software, and to prepare derivative works of the
 *   Software, and to permit third-parties to whom the Software is furnished to
 *   do so, all subject to the following:
 *
 *   The copyright notices in the Software and this entire statement, including
 *   the above license grant, this restriction and the following disclaimer,
 *   must be included in all copies of the Software, in whole or in part, and
 *   all derivative works of the Software, unless such copies or derivative
 *   works are solely in the form of machine-executable object code generated by
 *   a source language processor.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 *   SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 *   FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *   DEALINGS IN THE SOFTWARE.
 * 
 */

#include "gfx/gfx_context.hpp"
#include "gfx/pixel_array.hpp"
#include "gfx/window.hpp"
#include "gfx/window_listener.hpp"

#ifdef USE_SDL
#include "sdl/gfx/sdl_gfx_driver.hpp"
#include "sdl/timer/sdl_timer.hpp"
#elif defined(USE_XNA)
#include "xna_gfx_driver.hpp"
#include "xna_timer.hpp"
#endif

#include "boost/shared_ptr.hpp"

#include <cmath>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <list>


using namespace boost;
using namespace Coercri;
using namespace std;

// Constants

const int DT_MSEC = 10;
const float DT = float(DT_MSEC) / 1000;

const float BRICK_SIZE[2] = { 30, 20 };

const float BAT_SIZE[2] = { 75, 10 };
const float BALL_RADIUS = 5;

const float PLAYFIELD_SIZE[2] = { 25*BRICK_SIZE[0], 25*BRICK_SIZE[1] };

const int BAT_Y = int(PLAYFIELD_SIZE[1] - 3*BRICK_SIZE[1]);

const int BAT_MIN_X = 0;
const int BAT_MAX_X = int(PLAYFIELD_SIZE[0] - BAT_SIZE[0]);

const float PI = 4 * atan(1.0f);

inline float DEG_TO_RAD(float deg) { return deg / 180.0f * PI; }


// Bricks

struct Brick {
    float x;
    float y;
};


// Game class -- handles the game itself

class Game : public WindowListener {
public:

    explicit Game(GfxDriver &drv)
        : bat_pos(50), ball_speed(450), window_closed(false), game_running(false), last_time(0), window_active(true)
    {

        // create brick graphic
        const Color
            light(255, 180, 180),
            mid  (255,   0,   0),

            dark (128,   0,   0);

        const int bw = int(BRICK_SIZE[0]), bh = int(BRICK_SIZE[1]);

        shared_ptr<PixelArray> parr(new PixelArray(bw, bh));
        for (int i = 0; i < bw; ++i) {

            for (int j = 0; j < bh; ++j) {

                if (i==0 || j == 0) {
                    (*parr)(i,j) = light;
                } else if (i==bw-1 || j==bh-1) {
                    (*parr)(i,j) = dark;
                } else {
                    (*parr)(i,j) = mid;
                }
            }
        }

        brick_graphic = drv.createGraphic(parr);

        // create ball graphic
        const Color ball_color(255, 255, 255);

        const int ball_size = int(BALL_RADIUS + 1);

        parr.reset(new PixelArray(2*ball_size+1, 2*ball_size+1));

        for (int i = -ball_size; i <= ball_size; ++i) {

            for (int j = -ball_size; j <= ball_size; ++j) {

                const float r = std::sqrt(float(i*i+j*j));

                
                // Make alpha vary from 255 at ball_radius - 0.5 to 0 at ball_radius + 0.5
                Color col(ball_color);
                if (r < BALL_RADIUS - 0.5f) {

                    col.a = 255;
                } else if (r > BALL_RADIUS + 0.5f) {

                    col.a = 0;
                } else {
                    col.a = (unsigned char)(((BALL_RADIUS+0.5f)-r)*255);
                }

                (*parr)(i+ball_size, j+ball_size) = col;
            }
        }

        ball_graphic = drv.createGraphic(parr, ball_size, ball_size);

        // create bat graphic
        const Color
            blight(255, 255, 255),

            bmid  (190, 190, 190),
            bdark (128, 128, 128);

        const int batw = int(BAT_SIZE[0]), bath = int(BAT_SIZE[1]);

        parr.reset(new PixelArray(batw, bath));
        for (int i = 0; i < batw; ++i) {

            for (int j = 0; j < bath; ++j) {

                if (i==0 || j==0) {
                    (*parr)(i,j) = blight;
                } else if (i==batw-1 || j==bath-1) {
                    (*parr)(i,j) = bdark;
                } else {
                    (*parr)(i,j) = bmid;
                }
            }
        }

        bat_graphic = drv.createGraphic(parr);
    }

    void setupLevel()
    {

        ball_pos[0] = 10;
        ball_pos[1] = 200;

        ball_vel[0] = sqrt(0.5f) * ball_speed;
        ball_vel[1] = sqrt(0.5f) * ball_speed;

        for (int i = 2; i < int(PLAYFIELD_SIZE[0]/BRICK_SIZE[0])-2; ++i) {

            Brick b;
            b.x = i*BRICK_SIZE[0];

            b.y = 3*BRICK_SIZE[1];
            bricks.push_back(b);

            b.y = 7*BRICK_SIZE[1];
            bricks.push_back(b);
        }
    }

    // clips the line (x,y)+lambda*(dx,dy), lambda_min<=lambda<=lambda_max
    // against the plane nx*x + ny*y >= a.
    // sets changed_min and/or changed_max to TRUE if lambda_min or lambda_max was changed.
    // sets rejected to TRUE and leaves lambda_min, lambda_max unchanged if the line is clipped out totally.
    
    void clipLine(float x, float y, float dx, float dy,

                  float &lambda_min, float &lambda_max,
                  float nx, float ny, float a,

                  bool &changed_min, bool &changed_max, bool &rejected)
    {
        const float tol = 1e-4;

        
        changed_min = changed_max = rejected = false;

        const float dist_at_min = nx*(x+lambda_min*dx) + ny*(y+lambda_min*dy) - a;

        const float dist_at_max = nx*(x+lambda_max*dx) + ny*(y+lambda_max*dy) - a;

        if (dist_at_min < -tol && dist_at_max < -tol) {

            // Both endpoints are outside the good region. Reject.
            rejected = true;
        } else if (dist_at_min > tol && dist_at_max > tol) {

            // Both endpoints are inside the half-region.
            // Do nothing.
        } else if (fabs(dist_at_min) < tol && fabs(dist_at_max) < tol) {

            // The line segment is parallel to one of the boundaries. We let this one go.
            // Do nothing.
        } else {
            // One endpoint is inside and one is outside.
            const float lambda_intercept = (a - nx*x - ny*y) / (nx*dx + ny*dy);

            if (dist_at_min > tol || dist_at_max < -tol) {

                // MIN is on the good side, MAX on the bad side, so clip max
                lambda_max = lambda_intercept;
                changed_max = true;
            } else {

                // MAX is on the good side, MIN on the bad side, so clip min
                lambda_min = lambda_intercept;
                changed_min = true;
            }
        }
    }

    
    // returns axis==-1, lambda=1 on miss
    // on hit, returns lambda as hit point, and axis=0 if hit left/right side, or axis=1 if hit top/bottom side.
    void findCollision(float x, float y, float dx, float dy,

                       float bx, float by, float bw, float bh,

                       float &lambda, int &axis)
    {
        float lambda_min = 0, lambda_max = 1;

        // Clip line (x,y) + lambda*(dx,dy) to the interior of the brick.

        bool changed_min, changed_max, rejected;
        axis = -1;

        
        clipLine(x, y, dx, dy, lambda_min, lambda_max,  1,  0,  bx,    changed_min, changed_max, rejected);

        if (changed_min) axis = 0;
        if (!rejected) {

            clipLine(x, y, dx, dy, lambda_min, lambda_max, -1,  0, -bx-bw, changed_min, changed_max, rejected);

            if (changed_min) axis = 0;
            if (!rejected) {

                clipLine(x, y, dx, dy, lambda_min, lambda_max,  0,  1,  by,    changed_min, changed_max, rejected);

                if (changed_min) axis = 1;
                if (!rejected) {

                    clipLine(x, y, dx, dy, lambda_min, lambda_max,  0, -1, -by-bh, changed_min, changed_max, rejected);

                    if (changed_min) axis = 1;
                }
            }
        }

        if (rejected) {

            // Line was totally clipped out. We missed the brick.
            lambda = 1;
            axis = -1;
        } else if (axis == -1) {

            // This means we started inside the brick. Let it go
            lambda = 1;
            axis = -1;
        } else {

            lambda = lambda_min;
            // axis was set above.
        }
    }
    
    void moveBall(float amount)
    {

        for (int ntries = 0; ntries < 100; ++ntries) {

            // This is the line segment that we are trying to move the ball through
            const float x = ball_pos[0];

            const float y = ball_pos[1];
            const float dx = ball_vel[0] * amount;

            const float dy = ball_vel[1] * amount;

            
            // Check for collisions with bricks.
            // Note, v. inefficient algorithm, we literally just loop through the bricks and check each one.
            // It would be better to use some sort of culling first (ie only check bricks which are "near" the ball).
            
            std::list<Brick>::iterator which_brick = bricks.end();

            int coll_axis = -1;
            float best_lambda = 1;

            bool hit_bat = false;
            
            for (std::list<Brick>::iterator it = bricks.begin(); it != bricks.end(); ++it) {

                float lambda;
                int axis;
                findCollision(x, y, dx, dy, it->x, it->y, BRICK_SIZE[0], BRICK_SIZE[1], lambda, axis);

                if (axis != -1 && lambda < best_lambda) {
                    best_lambda = lambda;

                    coll_axis = axis;
                    which_brick = it;
                }
            }

            // Check for collision with bat
            {
                float lambda;
                int axis;

                findCollision(x, y, dx, dy, bat_pos, BAT_Y, BAT_SIZE[0], BAT_SIZE[1], lambda, axis);

                if (lambda < best_lambda) {
                    best_lambda = lambda;

                    coll_axis = axis;
                    which_brick = bricks.end();

                    hit_bat = true;
                }
            }
        
            // Move the ball by best_lambda.
            for (int i = 0; i < 2; ++i) ball_pos[i] += ball_vel[i] * amount * best_lambda;

            // Check for out-of-bounds, and reflect velocity if necessary
            for (int i = 0; i < 2; ++i) {

                if (ball_pos[i] < 0) ball_vel[i] = fabs(ball_vel[i]);

                if (ball_pos[i] > PLAYFIELD_SIZE[i]) ball_vel[i] = -fabs(ball_vel[i]);
            }

            
            // If there is no collision then we are done
            if (coll_axis == -1) break;

            // OK we have to do a collision with either a brick or the bat.

                
            if (hit_bat && coll_axis==1 && ball_vel[1] > 0) {

                // bounce off the top edge of the bat.

                const float INTERP_FACTOR = 0.6f;
                const float MIN_ANGLE = DEG_TO_RAD(20);

                const float MAX_ANGLE = DEG_TO_RAD(70);
                
                // work out current ball angle.
                float angle = atan(ball_vel[0]/ball_vel[1]);

                const float absangle = abs(angle);
                
                // work out target angle (based on where it hit the bat).
                const float mu = 2*((ball_pos[0] - bat_pos) / BAT_SIZE[0]) - 1;  // from -1 to 1

                const float absmu = fabs(mu);
                const float sgnmu = mu<0 ? -1 : 1;

                float target_absangle;
                if (absmu < 0.3f) target_absangle = DEG_TO_RAD(15);

                else if (absmu < 0.7f) target_absangle = DEG_TO_RAD(45);

                else target_absangle = DEG_TO_RAD(75);
                
                // interpolate current and target to get the post-bounce angle.
                const float new_absangle = (1-INTERP_FACTOR) * absangle + INTERP_FACTOR * target_absangle;

                angle = sgnmu * new_absangle;
                
                // apply min/max limits.
                if (angle < -MAX_ANGLE) angle = -MAX_ANGLE;

                else if (angle > -MIN_ANGLE && angle <= 0) angle = -MIN_ANGLE;

                else if (angle >= 0 && angle < MIN_ANGLE) angle = MIN_ANGLE;

                else if (angle > MAX_ANGLE) angle = MAX_ANGLE;

                    
                // set ball_vel accordingly
                ball_vel[0] = sin(angle) * ball_speed;
                ball_vel[1] = -cos(angle) * ball_speed;
                
            } else {

                // bounce off a brick (or side of the bat or something).
                ball_vel[coll_axis] = -ball_vel[coll_axis];
                if (which_brick != bricks.end()) {

                    const float SPEEDUP = 1.01f;
                    bricks.erase(which_brick);

                    for (int i = 0; i < 2; ++i) ball_vel[i] *= SPEEDUP;

                    ball_speed *= SPEEDUP;
                }
            }

            amount *= (1-best_lambda);  // Loop again with reduced amount (for the rest of the timestep).

        }
    }

    // event handlers
    void onClose() { window_closed = true; }

    void onMouseMove(int x, int y)
    {
        bat_pos = x - BAT_SIZE[0]/2;

        if (bat_pos < BAT_MIN_X) bat_pos = BAT_MIN_X;

        if (bat_pos > BAT_MAX_X) bat_pos = BAT_MAX_X;
    }

    void onActivate()
    {
        window_active = true;
    }

    void onDeactivate()
    {

        window_active = false;
    }
    
    // update
    bool update(unsigned int time_now, Window &window)
    {

        if (!game_running) return false;
        if (time_now - last_time > (unsigned int) (DT_MSEC)) {

            if (window_active) {
                moveBall(DT);
                window.invalidateAll();
            }

            last_time += DT_MSEC;
            return true;
        } else {

            return false;
        }
    }

    void catchUp(unsigned int time_now) {

        while (time_now - last_time > (unsigned int) DT_MSEC) last_time += DT_MSEC;
    }


    // draw
    void draw(GfxContext &gc)
    {
        if (!game_running) return;

        
        gc.clearScreen(Color(0,0,0));
        
        gc.drawGraphic(int(ball_pos[0]), int(ball_pos[1]), *ball_graphic);

        for (std::list<Brick>::const_iterator it = bricks.begin(); it != bricks.end(); ++it) {

            gc.drawGraphic(int(it->x), int(it->y), *brick_graphic);
        }

        gc.drawGraphic(int(bat_pos), BAT_Y, *bat_graphic);
    }

    // miscellaneous functions
    bool windowClosed() const { return window_closed; }

    void startGame(Timer &timer)
    {
        game_running = true;

        setupLevel();
        last_time = timer.getMsec();
    }
    void stopGame() {

        game_running = false;
    }
    bool isRunning() const { return game_running; }

private:
    float ball_vel[2];
    float ball_pos[2];

    float bat_pos;

    float ball_speed;
    
    std::list<Brick> bricks;

    shared_ptr<Graphic> brick_graphic, ball_graphic, bat_graphic;

    bool window_closed;

    bool game_running;
    unsigned int last_time;

    bool window_active;
};




extern "C" int main(int argc, char **argv)
{

    try {

        // Initialize GfxDriver, Timer
#ifdef USE_SDL
	shared_ptr<GfxDriver> gfx_driver(new SDLGfxDriver);

        shared_ptr<Timer> timer(new SDLTimer);
#elif defined(USE_XNA)
	shared_ptr<GfxDriver> gfx_driver(new XNAGfxDriver);

	shared_ptr<Timer> timer(new XNATimer);
#endif
        
        // Create the Window
        shared_ptr<Window> my_window = gfx_driver->createWindow(750, 500, true, false, "Bouncy Ball Game");

        // Create the Game and add it to the window
        Game game(*gfx_driver);
        my_window->addWindowListener(&game);

	// Start the game
	game.startGame(*timer);
	
        // Main Loop.
        while (!game.windowClosed()) {

            // Process all pending events.
            bool did_something = false;
            while (gfx_driver->pollEvents()) did_something = true;

            // Run game update if needed
            int nupdates = 5;
            const unsigned int time_now = timer->getMsec();

            while (nupdates > 0 && game.update(time_now, *my_window)) {

                did_something = true;
                --nupdates;
            }
            // "catch up" the timer if necessary.
            game.catchUp(timer->getMsec());

            // If window is invalid then repaint it
            if (my_window->needsRepaint()) {
                std::auto_ptr<GfxContext> gc = my_window->createGfxContext();

                game.draw(*gc);
                my_window->cancelInvalidRegion();
                did_something = true;
            }

            
            // If nothing was done then we should sleep for a bit.
            if (!did_something) timer->sleepMsec(10);
        }
        
    } catch (std::exception &e) {

        cout << "caught exception: " << e.what() << endl;
    } catch (...) {

        cout << "caught unknown exception" << endl;
    }
    
    return 0;
}