Skip to content

Instantly share code, notes, and snippets.

@Metapyziks
Created August 29, 2014 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Metapyziks/2d1104fcd0010ca07b36 to your computer and use it in GitHub Desktop.
Save Metapyziks/2d1104fcd0010ca07b36 to your computer and use it in GitHub Desktop.
Example game authored using the BudgetBoy API.
using System.Collections.Generic;
using GameAPI;
using GameAPI.BudgetBoy;
namespace SmashBlox.Stages
{
class AttractStage : Stage<Main>
{
private Demo _demo;
private int _frame;
private Sprite _insertCoin;
public AttractStage(Main game, Demo demo)
: base(game)
{
_demo = demo;
_demo.IsLooping = true;
var text = Graphics.GetImage("insertcoin");
var swatch = Graphics.Palette.FindSwatch(0xffffff, 0xffffff, 0xffffff);
_insertCoin = Add(new Sprite(text, swatch), 0);
_insertCoin.Position = (Graphics.Size - _insertCoin.Size) / 2;
StartCoroutine(Flashy);
}
private IEnumerator<IAwaitable> Flashy()
{
while (true) {
_insertCoin.IsVisible = true;
yield return Delay(0.5);
_insertCoin.IsVisible = false;
yield return Delay(0.5);
}
}
protected override void OnUpdate()
{
if (Controls.A.JustPressed || Controls.B.JustPressed ||
Controls.Start.JustPressed || Controls.Select.JustPressed ||
Controls.Horizontal.Value != 0 || Controls.Vertical.Value != 0) {
Game.Start();
}
base.OnUpdate();
}
protected override void OnRender()
{
Graphics.Render(_demo.GetFrameStream(_frame++));
base.OnRender();
}
}
}
using System.Linq;
using GameAPI.BudgetBoy;
namespace SmashBlox.Entities
{
class Ball : Entity<Main>
{
public Vector2f Velocity { get; set; }
private Sound _paddleHit;
private Sound _blockHit;
protected override void OnLoadAudio(Audio audio)
{
base.OnLoadAudio(audio);
_paddleHit = audio.GetSound("bounce2");
_blockHit = audio.GetSound("bounce");
}
protected override void OnLoadGraphics(Graphics graphics)
{
base.OnLoadGraphics(graphics);
var image = graphics.GetImage("ball");
var swatch = graphics.Palette.FindSwatch(0xffffff, 0xffffff, 0xffffff);
AddSprite(new Sprite(image, swatch), new Vector2i(-2, -2));
CalculateBounds();
}
public void Bounce(Vector2f normal)
{
Bounce(normal, 1f);
}
public void Bounce(Vector2f normal, float scale)
{
float dot = Velocity.Dot(normal);
if (dot >= 0f) return;
Velocity -= (1 + scale) * normal * dot;
}
protected override void OnUpdate(double dt)
{
base.OnUpdate(dt);
Position += Velocity * (float) dt;
if (X < 8f) {
X = 8f; Bounce(Vector2f.UnitX);
Stage.Audio.Play(_paddleHit, 1f, 1f);
} else if (X > Stage.Graphics.WidthPixels - 8f) {
X = Stage.Graphics.WidthPixels - 8f; Bounce(-Vector2f.UnitX);
Stage.Audio.Play(_paddleHit, 1f, 1f);
}
if (Y > Stage.Graphics.HeightPixels - 8f) {
Y = Stage.Graphics.HeightPixels - 8f; Bounce(-Vector2f.UnitY);
Stage.Audio.Play(_paddleHit, 1f, 1f);
}
var paddle = Stage.GetEntities<Paddle>().FirstOrDefault(x => x.Bounds.Intersects(Bounds));
if (paddle != null) {
Bounce(Vector2f.UnitY);
//Velocity += new Vector2f(X - paddle.Bounds.Center.X, 0f) * 64 / paddle.Bounds.Width;
Stage.Audio.Play(_paddleHit, 1f, 1f);
}
Vector2f normal; int phase;
if (Stage.GetEntity<BlockGrid>().CheckForCollision(this, out normal, out phase)) {
if (normal.X > 0) {
Bounce(Vector2f.UnitX);
} else if (normal.X < 0) {
Bounce(-Vector2f.UnitX);
}
if (normal.Y > 0) {
Bounce(Vector2f.UnitY);
} else if (normal.Y < 0) {
Bounce(-Vector2f.UnitY);
}
Stage.Audio.Play(_blockHit, 1f, 1f - Mathf.Log(phase, 10) / 2f);
}
}
}
}
using System;
using System.Collections.Generic;
using GameAPI.BudgetBoy;
namespace SmashBlox.Entities
{
class BlockGrid : Entity<Main>
{
private Tilemap _tiles;
private int[] _grid;
private Image _blockImage;
private Swatch[] _blockSwatches;
public int Phases { get { return _blockSwatches.Length; } }
public int Columns { get { return _tiles.Columns; } }
public int Rows { get { return _tiles.Rows; } }
public BlockGrid(int cols, int rows)
{
_tiles = new Tilemap(new Vector2i(16, 8), new Vector2i(cols, rows));
_grid = new int[cols * rows];
LocalBounds = new Bounds(0, 0, _tiles.Width, _tiles.Height);
}
protected override void OnLoadGraphics(Graphics graphics)
{
base.OnLoadGraphics(graphics);
_blockImage = graphics.GetImage("block");
_blockSwatches = new Swatch[] {
graphics.Palette.FindSwatch(0x0000FC, 0x0078F8, 0x3CBCFC),
graphics.Palette.FindSwatch(0x940084, 0xD800CC, 0xF878F8),
graphics.Palette.FindSwatch(0xA81000, 0xF83800, 0xF87858),
graphics.Palette.FindSwatch(0x503000, 0xAC7C00, 0xF8B800),
graphics.Palette.FindSwatch(0x007800, 0x00B800, 0xB8F818)
};
}
private bool IsColliding(Vector2f pos, out Vector2i block, ref int phase)
{
block = (Vector2i) pos;
if (pos.X >= 0f && pos.X < Columns && pos.Y >= 0f && pos.Y <= Rows) {
var p = this[(int) pos.X, (int) pos.Y];
phase = Math.Max(p, phase);
return p > 0;
}
return false;
}
public bool CheckForCollision(Ball ball, out Vector2f normal, out int phase)
{
normal = Vector2f.Zero;
phase = 0;
if (!ball.Bounds.Intersects(Bounds)) return false;
var bounds = (ball.Bounds - Position) / new Vector2f(_tiles.TileWidth, _tiles.TileHeight);
var collided = new List<Vector2i>();
Vector2i pos;
if (IsColliding(bounds.TopLeft, out pos, ref phase)) {
normal += new Vector2f(1f, -1f);
if (!collided.Contains(pos)) collided.Add(pos);
}
if (IsColliding(bounds.TopRight, out pos, ref phase)) {
normal += new Vector2f(-1f, -1f);
if (!collided.Contains(pos)) collided.Add(pos);
}
if (IsColliding(bounds.BottomLeft, out pos, ref phase)) {
normal += new Vector2f(1f, 1f);
if (!collided.Contains(pos)) collided.Add(pos);
}
if (IsColliding(bounds.BottomRight, out pos, ref phase)) {
normal += new Vector2f(-1f, 1f);
if (!collided.Contains(pos)) collided.Add(pos);
}
foreach (var tile in collided) {
this[tile.X, tile.Y] -= 1;
}
if (normal.LengthSquared > 0) {
normal = normal.Normalized;
return true;
}
return false;
}
public int this[int col, int row]
{
get { return _grid[col + row * Columns]; }
set
{
if (value < 0 || value > Phases) {
throw new ArgumentOutOfRangeException();
}
int index = col + row * Columns;
if (_grid[index] == value) return;
_grid[index] = value;
if (value > 0) {
_tiles.SetTile(col, row, _blockImage, _blockSwatches[value - 1]);
} else {
_tiles.ClearTile(col, row);
}
}
}
protected override void OnRender(Graphics graphics)
{
_tiles.X = Mathf.FloorToInt(X);
_tiles.Y = Mathf.FloorToInt(Y);
_tiles.Render(graphics);
}
}
}
using System.Collections.Generic;
using GameAPI.BudgetBoy;
using SmashBlox.Entities;
namespace SmashBlox.Stages
{
class GameStage : Stage<Main>
{
private BlockGrid _blocks;
private Paddle _paddle;
private Ball _ball;
public GameStage(Main game)
: base(game)
{
Graphics.SetClearColor(Graphics.Palette.FindClosest(0x000000));
_paddle = Add(new Paddle {
X = Graphics.WidthPixels / 2,
Y = 8
}, 0);
_ball = Add(new Ball(), 1);
_blocks = Add(new BlockGrid(12, 8) {
X = 4,
Y = Graphics.HeightPixels - 68
}, 0);
for (int y = 0; y < _blocks.Rows; ++y) {
for (int x = 0; x < _blocks.Columns; ++x) {
if ((x + (y / 2) * 3 + 1) % 6 <= 1) continue;
_blocks[x, y] = 1 + (y % _blocks.Phases);
}
}
StartCoroutine(MainCoroutine);
}
private IEnumerator<IAwaitable> MainCoroutine()
{
for (;;) {
_ball.Velocity = new Vector2f(0f, 0f);
_ball.Y = _paddle.Y + 8;
do {
_ball.X = _paddle.NextX;
yield return null;
} while (!Controls.A.JustPressed);
_ball.Velocity = new Vector2f(_paddle.NextX > _paddle.X ? 1f : -1f, 1.5f) * 64f;
while (_ball.Y > 4f) {
yield return null;
}
Audio.Play(Audio.GetSound("miss"), 1, 1);
}
}
}
}
using GameAPI;
using GameAPI.BudgetBoy;
using ResourceLibrary;
using SmashBlox.Stages;
namespace SmashBlox
{
public class Main : Game<Main>
{
private Demo _demo;
public Main() : base(60, 200, 160) { }
protected override void OnSetupGameInfo(GameAPI.GameInfo info)
{
info.AuthorName = "Facepunch";
info.Title = "Smash Blox";
info.Description = "Like Breakout but cheaper!";
}
protected override void OnLoadResources(ResourceVolume volume)
{
base.OnLoadResources(volume);
_demo = volume.Get<Demo>("attract");
}
protected override void OnInitialize()
{
base.OnInitialize();
SetStage(new AttractStage(this, _demo));
}
public void Start()
{
SetStage(new GameStage(this));
}
}
}
using GameAPI;
using GameAPI.BudgetBoy;
namespace SmashBlox.Entities
{
class Paddle : Entity<Main>
{
private Image _end;
private Image _mid;
private Sprite _leftEndSprite;
private Sprite _rightEndSprite;
private Sprite[] _midSprites;
private Swatch _swatch;
private int _size;
private bool _sizeChanged;
private Axis _moveAxis;
public int Size
{
get { return _size; }
set
{
_size = value;
_sizeChanged = true;
LocalBounds = new Bounds(-Size * 4 - 2, -2, Size * 4 + 2, 2);
}
}
public float NextX
{
get
{
var dx = (float) (_moveAxis.Value * MoveSpeed * Stage.Timestep);
var margin = 8f + Size * 4f;
return Mathf.Clamp(X + dx, margin, Stage.Graphics.WidthPixels - margin);
}
}
public float MoveSpeed { get; set; }
public Paddle()
{
Size = 4;
MoveSpeed = 128f;
}
private void Resize()
{
if (_leftEndSprite == null) return;
_sizeChanged = false;
SetSpriteOffset(_leftEndSprite, new Vector2i(-Size * 4 - _leftEndSprite.Width, -2));
SetSpriteOffset(_rightEndSprite, new Vector2i(Size * 4, -2));
if (_midSprites != null) {
foreach (var sprite in _midSprites) {
RemoveSprite(sprite);
}
}
_midSprites = new Sprite[Size];
for (int i = 0; i < _midSprites.Length; ++i) {
_midSprites[i] = AddSprite(new Sprite(_mid, _swatch), new Vector2i(-Size * 4 + i * 8, -2));
}
}
protected override void OnLoadGraphics(Graphics graphics)
{
base.OnLoadGraphics(graphics);
_end = graphics.GetImage("paddle", "end");
_mid = graphics.GetImage("paddle", "mid");
_swatch = graphics.Palette.FindSwatch(0xffffff, 0xffffff, 0xffffff);
_leftEndSprite = AddSprite(new Sprite(_end, _swatch));
_rightEndSprite = AddSprite(new Sprite(_end, _swatch) { FlipX = true });
}
protected override void OnEnterStage(Stage<Main> stage)
{
base.OnEnterStage(stage);
_moveAxis = stage.Controls.Horizontal;
}
protected override void OnUpdate(double dt)
{
base.OnUpdate(dt);
X = NextX;
}
protected override void OnRender(Graphics graphics)
{
if (_sizeChanged) Resize();
base.OnRender(graphics);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment