Brick Breaker Clone Made Entirely on my iPhone!
I made a pretty simple Brick Breaker clone using an IDE called Continuous Development Environment for iOS.
I made the game almost entirely on the bus, lunch breaks, 10 minute breaks, and various other times I had a few free minutes to kill.
I learned a little bit about SceneKit and some of the other Xamarian libraries proprietary to iOS.
I have a short little video demonstrating gameplay and going over a little bit of the script in the IDE that I made the game.
This was primarily done as a proof of concept that I could make a game for my phone on my phone. I have made one other game that is a Helicopter Game clone with the Continuous IDE.
The code is included below (It is still in development, so there are unused functions)
using System;
using CoreGraphics; using SceneKit; using UIKit;
public int lives = 3; int score = 0; public int brickCount = 8; public int speed = 60; public bool flash;
static class GameDefaults { public static float size = 1.5f; public static float brickSize = 2f; public static UIColor color = UIColor.Yellow; public static UIColor redColor = UIColor.Red; public static UIColor blackColor = UIColor.Black; public static UIColor brickColor = UIColor.Blue; public static float restitution = 2.01f; public static float height = 30.0f; }
class Ball { public readonly SCNNode Node; public Ball() { var geom = SCNSphere.Create(GameDefaults.size); geom.FirstMaterial.Diffuse.ContentColor = GameDefaults.color; Node = SCNNode.FromGeometry(geom);
// Attach a light to the ball
var ln = SCNNode.Create();
ln.Light = SCNLight.Create();
ln.Position = new SCNVector3(0,0,GameDefaults.size*4);
Node.Add(ln);
Node.Position = new SCNVector3(0,10,0);
// Add physics to the ball
Node.PhysicsBody = SCNPhysicsBody.CreateDynamicBody();
Node.PhysicsBody.Damping = 0;
Node.PhysicsBody.Friction = 0;
Node.PhysicsBody.Restitution = GameDefaults.restitution;
Node.PhysicsBody.AffectedByGravity = false;
var options = new SCNPhysicsShapeOptions();
Node.PhysicsBody.PhysicsShape = SCNPhysicsShape.Create(geom, options);
Node.PhysicsBody.CategoryBitMask = 1u;
Node.PhysicsBody.CollisionBitMask = 1u;
Node.PhysicsBody.ContactTestBitMask = 1u;
}
}
class Paddle { public readonly SCNNode Node; public Paddle(float w, float h, float y) { var geom = SCNBox.Create(w, h, 20, 0); geom.FirstMaterial.Diffuse.ContentColor = UIColor.LightGray; geom.FirstMaterial.Specular.ContentColor = UIColor.Yellow; geom.FirstMaterial.Specular.Intensity = 1; Node = SCNNode.FromGeometry(geom); Node.Position = new SCNVector3(0,y,0); Node.PhysicsBody = SCNPhysicsBody.CreateStaticBody(); var options = new SCNPhysicsShapeOptions(); Node.PhysicsBody.PhysicsShape = SCNPhysicsShape.Create(geom, options); Node.PhysicsBody.CategoryBitMask = 1u; Node.PhysicsBody.CollisionBitMask = 1u; Node.PhysicsBody.ContactTestBitMask = 1u; } }
class Brick { public readonly SCNNode Node; public Brick(float x, float y) { var geom = SCNBox.Create(5f, 5, 10,0); geom.FirstMaterial.Diffuse.ContentColor = UIColor.FromRGB(135, 206, 250); geom.FirstMaterial.Specular.ContentColor = UIColor.Yellow; geom.FirstMaterial.Specular.Intensity = 1; Node = SCNNode.FromGeometry(geom); Node.Position = new SCNVector3(x,y,0); Node.PhysicsBody = SCNPhysicsBody.CreateStaticBody(); var options = new SCNPhysicsShapeOptions(); Node.PhysicsBody.PhysicsShape = SCNPhysicsShape.Create(geom, options); Node.PhysicsBody.CategoryBitMask = 1u; Node.PhysicsBody.CollisionBitMask = 1u; Node.PhysicsBody.ContactTestBitMask = 1u; } }
class Wall { public readonly SCNNode Node; public Wall(float w, float h, float x, float y) { var geom = SCNBox.Create(w, h, 1, 0); geom.FirstMaterial.Diffuse.ContentColor = UIColor.DarkGray; geom.FirstMaterial.Specular.ContentColor = UIColor.Yellow; geom.FirstMaterial.Specular.Intensity = 0f; Node = SCNNode.FromGeometry(geom); var ln = SCNNode.Create(); ln.Light = SCNLight.Create(); ln.Position = new SCNVector3(0,0,GameDefaults.size*4); Node.Add(ln); Node.Position = new SCNVector3(x,y,0); Node.PhysicsBody = SCNPhysicsBody.CreateStaticBody(); var options = new SCNPhysicsShapeOptions(); Node.PhysicsBody.PhysicsShape = SCNPhysicsShape.Create(geom, options); Node.PhysicsBody.CategoryBitMask = 1u; Node.PhysicsBody.CollisionBitMask = 1u; Node.PhysicsBody.ContactTestBitMask = 1u;
}
}
class Camera { public readonly SCNNode Node = SCNNode.Create(); readonly SCNCamera camera = SCNCamera.Create(); public Camera() { Node.Camera = camera; Node.Position = new SCNVector3 (0,0,150); camera.ZNear = 1; camera.ZFar = 150; // Attach a light to the camera var ln = SCNNode.Create(); ln.Light = SCNLight.Create(); ln.Position = new SCNVector3(0,0,50); Node.Add(ln); } }
var scoreLabel = new UILabel() { Text = "0",
TextAlignment = UITextAlignment.Center,
Font = UIFont.SystemFontOfSize(25),
TextColor = UIColor.White.ColorWithAlpha(0.25f),
AutoresizingMask = UIViewAutoresizing.FlexibleDimensions,
AdjustsFontSizeToFitWidth = true,
};
void ResetPauseBall() { ball.Node.Position = new SCNVector3(0, 10, 0); ball.Node.PhysicsBody.Velocity = new SCNVector3(0, 0, 0); ball.Node.PhysicsBody.ResetTransform();
}
void ResetBallPosition() { if(ball.Node.Hidden == true) { ball.Node.Hidden = false; } if(lives<=0) data-preserve-html-node="true" { ball.Node.Hidden = true; } if(lives>0) { var rand = new Random(); ball.Node.Position = new SCNVector3(0, 10, 0); ball.Node.PhysicsBody.Velocity = new SCNVector3(rand.Next(-30,30), speed, 0); ball.Node.PhysicsBody.ResetTransform(); } }
void ChangeBrickColor() { //brick_1.Node.Geometry.FirstMaterial.Diffuse.ContentColor = UIColor.Yellow;
}
void DecreaseScore() { lives--;
if(lives<=0)
{
scoreLabel.BeginInvokeOnMainThread(() => {
scoreLabel.Text = $"Game Over";
});
}
if(lives>0)
{
scoreLabel.BeginInvokeOnMainThread(() => {
scoreLabel.Text =$"Level " + brickCount/8 + " " + lives+$"UP";
});
}
}
void StartGame() { //PlaceBricks();
PlaceBricksLoop(brickCount);
lives=3;
scoreLabel.BeginInvokeOnMainThread(() => {
scoreLabel.Text =$"Level " + brickCount/8 + " " + lives+$"UP";
});
scene.Background.ContentColor = GameDefaults.blackColor;
}
void CheckForRestart() { if(lives<=0) data-preserve-html-node="true" { score = 0; brickCount = 8; ResetBricks();
StartGame();
}
}
void PlaceBricksLoop(int brickCounter) { int xPos = -20; int yPos = 70;
for (int i = 0; i < brickCount; i++)
{
bricks[i] = new Brick(xPos +(int)2.5,yPos);
root.Add(bricks[i].Node);
xPos +=5;
if(xPos > 0 && xPos % 20 == 0)
{
xPos = -20;
yPos -= 5;
}
}
}
void ResetBricks() { foreach(Brick element in bricks) { if(element.Node.Hidden == false) { element.Node.Hidden = true; } } }
void CheckForBricks() { score++; if(score>=brickCount) { brickCount +=8; score = 0; bricks = new Brick[brickCount]; //ResetBricks(); PlaceBricksLoop(brickCount); ResetBallPosition(); scoreLabel.BeginInvokeOnMainThread(() => { scoreLabel.Text =$"Level " + brickCount/8 + " " + lives+$"UP"; }); } }
void BackroundFlash() { if(scene.Background.ContentColor == UIColor.Black) { scene.Background.ContentColor = GameDefaults.redColor; } else { scene.Background.ContentColor = GameDefaults.blackColor; }
}
var cam = new Camera(); var paddle = new Paddle(10f,2f,5); var ceiling = new Wall(100, 3, 0, 80); var floor = new Wall(100, 3, 0, -20); var leftWall = new Wall(3,100,-30,30); var rightWall = new Wall(3,100,30,30); var ball = new Ball(); var bricks = new Brick[brickCount];
var scene = SCNScene.Create(); //scene.PhysicsWorld.Gravity = SCNVector3.One; scene.PhysicsWorld.Gravity = SCNVector3.Zero;
var root = scene.RootNode;
var sceneView = new SCNView(new CGRect(0,0,320,320)); sceneView.BackgroundColor = UIColor.Black; sceneView.Scene = scene;
scoreLabel.Frame = sceneView.Bounds; sceneView.AddSubview(scoreLabel);
scene.PhysicsWorld.DidBeginContact += (s,e) => {
var otherNode = e.Contact.NodeA == ball.Node ? e.Contact.NodeB :
(e.Contact.NodeB == ball.Node ? e.Contact.NodeA : null);
if (otherNode == floor.Node) {
Console.WriteLine("HIT FLOOR");
//BackroundFlash();
DecreaseScore();
ResetBallPosition();
}
if (otherNode == paddle.Node) {
Console.WriteLine("HIT PADDLE");
}
if (otherNode == ceiling.Node) {
Console.WriteLine("HIT CEILING");
}
foreach(Brick element in bricks)
{
if (otherNode == element.Node) {
Console.WriteLine("HIT element");
element.Node.Hidden = true;
//element.Node.RemoveFromParentNode();
CheckForBricks();
}
}
// e.Contact.NodeA.Geometry.FirstMaterial.Diffuse.ContentColor=UIColor.Red; };
var panGesture = new UIPanGestureRecognizer (g => { for (var i = 0; i < g.NumberOfTouches; i++) { var viewLoc = g.LocationOfTouch(i, sceneView); var pos = sceneView.UnprojectPoint(new SCNVector3((float)viewLoc.X, (float)viewLoc.Y, 1.0f));
paddle.Node.Position = new SCNVector3(pos.X/8, 5, 0.0f);
paddle.Node.PhysicsBody.ResetTransform();
}
});
var tapGesture = new UITapGestureRecognizer (g => { CheckForRestart(); ResetBallPosition(); }); tapGesture.NumberOfTapsRequired = 2;
root.Add(paddle.Node); root.Add(floor.Node); root.Add(ceiling.Node); root.Add(leftWall.Node); root.Add(rightWall.Node); root.Add(ball.Node);
//ball.Node.PhysicsBody.ApplyForce(new SCNVector3(10,50,0), true); sceneView.AddGestureRecognizer(panGesture); sceneView.AddGestureRecognizer(tapGesture);
//ChangeBrickColor();
StartGame();
var Main = sceneView;