Hmm.. perhaps I should write a very simple game with the current engine and post the source code for the game. That would convince people a bit.
The code that I write in general is quite robust as I debug every piece of code that I have written.
Small old school arcade game coming soon: 'Bounce!'Edit:
Bounce! is very simple old school archade game written just in three days.
ScreenshotDownloadSource code for the game. I packed the game into just one .cpp file
/**
* (C) Jarmo A Tiitto. All rights reserved.
* This is the small game named Bounce!
* It mimics pong and breakout a litle.
*/
#include <RenderSubSystem/RenderSubSystem.h>
#include <RenderSubSystem/RendererWindow.h>
#include <RenderSubSystem/Renderer.h>
#include <RenderSubSystem/RenderingLayer.h>
#include <RenderSubSystem/RenderingManager.h>
#include <RenderSubSystem/Font.h>
#include <RenderSubSystem/TextGameObject.h>
#include <ObjectSubSystem/BaseCameraObject.h>
#include <ObjectSubSystem/EmptyObject.h>
#include <ObjectSubSystem/GraphicsObject.h>
#include <ObjectSubSystem/ObjectSubSystem.h>
#include <Math/Random.h>
#include <Math/Vector.hpp>
#include <General/cvString.h>
#include <General/SharedObjectPtr.h>
#include <algorithm>
#include <boost/chrono/chrono.hpp>
#include <ctime>
/**
* Game code.
*/
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
const float BALL_RADIUS = 20.0f;
const float PADDLE_Y_MAX = 150.0f;
class ScoreCounter;
class BoxTarget;
class BounceBall;
class Paddle;
// Score counter. Displays the score on screen.
class ScoreCounter
{
public:
int score;
ksObjects::GraphicsObject * gfxtext;
SharedObjectPtr<ksRenderer::Font> gfxfont;
std::string highscoretxt;
int highscore;
ScoreCounter(ksRenderer::Font * fnt)
: score(0), highscore(0), gfxtext(NULL), gfxfont(fnt, ksRenderer::Font::Disposer())
{
// Create general purpore graphics object.
gfxtext = ObjectSubSystem::instance().create<ksObjects::GraphicsObject>();
// Position it in our camera's coordinate space to top-left corner
gfxtext->position(ksMath::Vec2f(20.0f, 20.0f));
// Specialize the graphics to rendering text.
gfxtext->enableTextObject();
ksRenderer::TextGameObject & text = gfxtext->text();
highscoretxt = "\nI bet you can't go over 50!";
// Set font face for the text.
text.setFont(gfxfont.get());
// Define text style that is 64 pixels high.
int gfxstyle = text.createStyle(64);
// Precache style (optional)
text.loadStyles();
// Set default style and specify the text to be rendered.
text.setDefaultStyle(gfxstyle);
text.setText("Score: 0" + highscoretxt);
// Specify control point of the text
// and specify transformation.
text.centerText(-1, -1);
text.offset(ksMath::Vec2f(0.0f, 96.0f));
text.scale(ksMath::Vec2f(1.0f, -1.0));
}
~ScoreCounter()
{
// we need to delete the object.
ObjectSubSystem::instance().deleteObject(gfxtext);
}
void pointsUp()
{
++score;
gfxtext->text().setText("Score: " + stringify(score) + highscoretxt);
}
void pointsDown()
{
score = std::max(score - 2, 0);
gfxtext->text().setText("Score: " + stringify(score) + highscoretxt);
}
void reset()
{
score = 0;
highscore = 0;
gfxtext->text().setText("Score: " + stringify(score) + "\nLooooser! ;)");
}
void update()
{
highscore = std::max(highscore, score);
if(highscore >= 1)
{
highscoretxt = "\nYour best: " + stringify(highscore);
gfxtext->text().setText("Score: " + stringify(score) + highscoretxt);
}
}
bool gameover()
{
return highscore > 0 && score == 0;
}
};
// The box you have to hit with the ball
class BoxTarget
{
public:
bool hit;
ksMath::Vec2f position;
ksMath::Vec2f size;
BoxTarget()
: hit(false)
{
generate();
}
void generate()
{
size.x = 10.0f + ksMath::RandFloat() * 100.0f;
size.y = 10.0f + ksMath::RandFloat() * 80.0f;
position.x = 50.0f + ksMath::RandFloat() * (700.0f - size.x);
position.y = BALL_RADIUS * 2 + 10.0f + ksMath::RandFloat() * (SCREEN_HEIGHT - PADDLE_Y_MAX - BALL_RADIUS * 2 - 10.0f - size.y);
}
void interact(const BounceBall & ball, ScoreCounter & score);
};
class BounceBall
{
public:
ksMath::Vec2f position;
ksMath::Vec2f speed;
BounceBall()
{
reset();
}
void reset()
{
position.x = 100.0f + ksMath::RandFloat() * 600.0f;
position.y = 200.0f + ksMath::RandFloat() * 100.0f;
speed.y = -10.0f;
speed.x = -15.0f + ksMath::RandFloat() * 30.0f;
}
void update()
{
speed += ksMath::Vec2f(0.0f, 0.5f);
speed *= 0.999f;
position += speed;
if(position.x < BALL_RADIUS)
{
position.x = BALL_RADIUS;
speed.x = -speed.x * 0.9f;
}
if(position.x > SCREEN_WIDTH - BALL_RADIUS)
{
position.x = SCREEN_WIDTH - BALL_RADIUS;
speed.x = -speed.x * 0.9f;
}
}
void interact(ScoreCounter & score);
};
class Paddle
{
public:
ksMath::Vec2f position;
ksMath::Vec2f prevposition;
ksMath::Vec2f size;
ksMath::Vec2f speed;
float inertia;
float slowness;
Paddle(float width, float height, float _slowness)
: size(width, height), slowness(_slowness)
{
position.x = SCREEN_WIDTH / 2 - width / 2;
position.y = SCREEN_HEIGHT - height;
prevposition = position;
speed = ksMath::Vec2f(0.0f);
inertia = 0.0f;
}
void update()
{
prevposition = position;
int mx, my;
// Currently there is no InputSubSystem, so we must use GLFW directly.
glfwGetMousePos(&mx, &my);
ksMath::Vec2f currentpos, movement;
currentpos.x = mx - size.x / 2.0f;
currentpos.y = std::max((float)my, SCREEN_HEIGHT - PADDLE_Y_MAX);
movement = (currentpos - prevposition);
inertia += (movement * ksMath::Vec2f(slowness, 1.0f)).x;
speed = ksMath::Vec2f(inertia, movement.y);
position = prevposition + movement * ksMath::Vec2f(slowness, 1.0f) + ksMath::Vec2f(inertia, 0.0f);
inertia *= 0.99f;
// limit paddle area
position.x = std::min(std::max(-size.x, position.x), (float)SCREEN_WIDTH);
position.y = std::min(std::max(SCREEN_HEIGHT - PADDLE_Y_MAX, position.y), (float)SCREEN_HEIGHT);
}
void interact(BounceBall & ball);
};
void BoxTarget::interact(const BounceBall & ball, ScoreCounter & score)
{
if(ball.position.x - BALL_RADIUS > position.x + size.x) return;
if(ball.position.x + BALL_RADIUS < position.x) return;
if(ball.position.y - BALL_RADIUS > position.y + size.y) return;
if(ball.position.y + BALL_RADIUS < position.y) return;
// ball and target intersect.
score.pointsUp();
generate();
}
void BounceBall::interact(ScoreCounter & score)
{
if(position.y > SCREEN_HEIGHT - BALL_RADIUS)
{
position.y = SCREEN_HEIGHT - BALL_RADIUS;
speed.y = -speed.y * 0.9f;
score.pointsDown();
}
}
void Paddle::interact(BounceBall & ball)
{
// Check if ball hits the paddle
if(ball.position.x + BALL_RADIUS > position.x + 5.0f
&& ball.position.x - BALL_RADIUS < position.x + size.x - 5.0f
&& ball.position.y + BALL_RADIUS > position.y
&& ball.position.y < position.y + size.y)
{
// hit top
ball.position.y = position.y - BALL_RADIUS;
ball.speed.y = -ball.speed.y + speed.y;
ball.speed.x = ball.speed.x * 0.9f + speed.x * 0.5f;
return;
}
}
/**
* The Engine handles program startup and catches exceptions
* from our game_main() procedure.
* This is not the only way and is not yet standardized.
*/
void game_main()
{
using namespace ksRenderer;
using namespace ksMath;
/// KiloSprite consists of several subsystems.
/// All subsystems are singletons and so can be accessed anywhere from the game code.
/// This is pretty standard way to begin the application:
RenderSubSystem & rendsys = RenderSubSystem::instance();
RendererWindow & rendwindow = RendererWindow::instance();
ObjectSubSystem & objsys = ObjectSubSystem::instance();
// Open window
rendsys.open(800, 600, "Bounce!", false);
// Grap renderer objects.
ksRenderer::Renderer * renderer = rendsys.getRenderer();
ksRenderer::RenderingLayer * layer = renderer->getDefaultLayer();
ksObjects::BaseCameraObject * camera = renderer->getDefaultCamera();
// And thats it. We have renderer, a layer that contains all graphics and single camera to view the
// game world.
// Use screen coordinate system where (0,0) is top-left corner of the window.
camera->useScreenCoordinates();
rendwindow.vsync(false);
srand(time(0));
{
// Load font face
// Certain objects in KiloSprite are derived from SharedObject utility class which eases
// the handling of resources.
SharedObjectPtr<ksRenderer::Font> fnt(new ksRenderer::Font(), ksRenderer::Font::Disposer());
fnt->load("media/Digital Sans EF Medium.ttf");
ScoreCounter score(fnt.get());
BoxTarget target;
BounceBall ball;
Paddle paddle(200.0f, 20.0f, 0.05f);
// Create "gameover" text object.
// First create the general purpose graphics container object or just graphicsobject.
// This is a ObjectSubSystem resource.
ksObjects::GraphicsObject * gameover = objsys.create<ksObjects::GraphicsObject>();
// Position it on the screen.
gameover->position(ksMath::Vec2f(400.0f, 300.0f));
// Define the graphics object as text. This allocates a RenderSubSystem TextGameObject object
// and adds it to the current active layer. The active layer can be selected by renderer->setScene(...)
// However this is not now necessary as renderer has a default layer alredy active.
gameover->enableTextObject();
// Grab refence to the RenderSubSystem object
ksRenderer::TextGameObject & text = gameover->text();
// Configure the text proberties so we get big red "Game Over!" text on the screen
text.setFont(fnt.get());
RGBAf colors[] = {RGBAf(1.0f, 0.0f, 0.0f, 1.0f),
RGBAf(1.0f, 0.0f, 0.0f, 1.0f),
RGBAf(1.0f, 0.0f, 0.0f, 1.0f),
RGBAf(1.0f, 0.0f, 0.0f, 1.0f)};
int gfxstyle = text.createStyle(140, 2, colors, 3.0f);
int gfxsmall = text.createStyle(50, 1, colors);
text.loadStyles();
text.setDefaultStyle(gfxstyle);
// Text object supports multiline text with diffrent styles (mainly sizes and colours).
std::string txtgameover = "Game Over!\n";
text.setText(txtgameover + "(Hit space to retry)");
text.applyStyle(gfxsmall, utf8::distance(txtgameover.begin(), txtgameover.end()));
text.centerText(0, 0);
text.scale(ksMath::Vec2f(1.0f, -1.5));
// hide the object so it is not rendered at first.
text.hide();
boost::chrono::steady_clock::time_point gamelogic_t0 = boost::chrono::steady_clock::now();
while(rendwindow.is_open())
{
boost::chrono::steady_clock::time_point gamelogic_t1 = boost::chrono::steady_clock::now();
if(gamelogic_t1 - gamelogic_t0 > boost::chrono::milliseconds(16))
{
gamelogic_t0 = gamelogic_t1;
// Run game logic
ball.update();
paddle.update();
ball.interact(score);
paddle.interact(ball);
target.interact(ball, score);
score.update();
if(score.gameover())
{
// Make the text visible.
gameover->text().visible();
if(glfwGetKey(GLFW_KEY_SPACE))
{
score.reset();
target.generate();
ball.reset();
gameover->text().hide();
}
}
}
// Perform simple drawing.
layer->draw().fetchMaterial(BLEND_ALPHA);
layer->draw().filledcircle(ball.position, BALL_RADIUS, RGBAf(1.0f, 1.0f, 1.0f, 1.0f), RGBAf(0.2f, 0.2f, 0.2f, 0.1f));
layer->draw().fetchMaterial(RGBAf(0.0f, 1.0f, 0.0f, 1.0f), BLEND_REPLACE);
layer->draw().filledbox(target.position, target.size);
layer->draw().fetchMaterial(RGBAf(0.6f, 0.6f, 0.6f, 1.0f), BLEND_REPLACE);
layer->draw().filledbox(paddle.position, paddle.size);
// Render the current active layer.
// Pixels go to the window as no offscreen rendering target is active.
renderer->render();
// Show the new image to the user.
rendwindow.swapbuffers();
}
// clean up.
objsys.deleteObject(gameover);
} // destroy game elements.
// close the render subsystem. This releases all grapics resources
rendsys.close();
}