Compare commits

..

10 commits

Author SHA1 Message Date
John Alanbrook bd7080d163 breakout works 2023-09-08 17:34:48 +00:00
John Alanbrook 22fb3de804 Change to ur prototype scheme 2023-09-08 06:26:36 +00:00
John Alanbrook 3b67b08683 Split out objects into own scripts 2023-09-07 21:46:56 +00:00
John Alanbrook 996dd96d50 Move input to input object system 2023-09-06 22:48:25 +00:00
John Alanbrook 52d08ec085 start menu fixed 2023-09-06 17:17:33 +00:00
John Alanbrook cbeab6af8b add readme. 2023-08-19 00:20:10 +00:00
John Alanbrook 9fe10e6057 break 2023-06-09 03:50:47 +00:00
John Alanbrook fb639c1d13 pinball 2023-06-08 22:27:51 +00:00
John Alanbrook d8420594b6 Missiles 2023-06-07 22:26:38 +00:00
John Alanbrook 003655b06a sounds 2023-06-07 17:43:43 +00:00
17 changed files with 310 additions and 247 deletions

14
ball.js Normal file
View file

@ -0,0 +1,14 @@
Object.assign(self, {
collider: circle2d.clone({radius:5}),
img: sprite.clone({path:"ball.png"}),
phys: 0,
elasticity: 1,
friction: 0,
tag: 'ball',
collide(hit) {
Sound.play("bump.wav");
},
});
self.img.pos = sprite.POS_MID;

View file

@ -3,8 +3,8 @@ var score = 0;
var highscore = 0;
var lives = 3;
Log.warn(gameobjects['brick'].boundingbox);
var brick = World.spawn(gameobjects['brick']);
/* Spawn & kill a brick to get its width and height */
var brick = prototypes.get_ur('brick').make(World);
var brickbb = brick.char.boundingbox;
var bwidth = brickbb.r - brickbb.l;
var bheight = brickbb.t-brickbb.b;
@ -16,16 +16,19 @@ var cols = 13;
var lvlwidth = bwidth*cols;
var lvlheight = 600;
//var frameworld = World
var f = World.spawn(gameobjects['edge2d']);
var f = prototypes.get_ur('edge2d').make(World);
f.edge2d.thickness = 10;
f.edge2d.cpoints = Geometry.box(lvlwidth+f.edge2d.thickness*2,lvlheight);
f.edge2d.cpoints = Geometry.box(lvlwidth+f.edge2d.thickness*2,lvlheight-30);
f.edge2d.degrees = 1;
f.scale = 1;
f.edge2d.sync();
var drawColor = Color.green.slice();
drawColor.a = 100;
f.draw = function() {
Debug.line(f.edge2d.points, Color.green, 0, f.edge2d.thickness*2);
Debug.line(f.edge2d.points, drawColor, 0, f.edge2d.thickness*2);
};
register_draw(f.draw,f);
Register.draw.register(f.draw,f);
var fitbricks = Math.floor(lvlwidth/bwidth);
@ -34,7 +37,7 @@ var wingame = function() {
Log.warn("WINNER");
Player.players[0].uncontrol(paddle);
register_gui(function() {
Register.gui.register(function() {
GUI.text("WINNER", Window.dimensions.scale(0.5));
});
}
@ -77,14 +80,14 @@ Color.Arkanoid.Powerups = {
gray: [143,143,143] /* 1up */
};
var lvl = JSON.parse(IO.slurp("lvlsparse.json"));
var lvl = JSON.parse(IO.slurp("lvl1.json"));
for (var row = 0; row < rows; row++) {
for (var col = 0; col < cols; col++) {
if (lvl.bricks[row][col] === 0)
continue;
var brick = World.spawn(gameobjects['brick']);
var brick = prototypes.get_ur('brick').make(World);
brick.powerup = function() {
if (Math.random() < 0.1)
spawn_random_powerup(this.pos);
@ -102,27 +105,28 @@ for (var row = 0; row < rows; row++) {
}
var flashtimer = timer.make(function() {
if (gameobjects['brick'].instances.length === 0) {
if (prototypes.get_ur('brick').instances.length === 0) {
flashtimer.kill();
return;
}
var idx = Math.randomint(gameobjects['brick'].instances.length);
gameobjects['brick'].instances[idx].flash();
var idx = Math.randomint(prototypes.get_ur('brick').instances.length);
prototypes.get_ur('brick').instances[idx].flash();
flashtimer.time = Math.random_range(1,3);
}, Math.random_range(1,3));
var paddle = World.spawn(gameobjects['paddle']);
var paddle = prototypes.get_ur('paddle').make(self);
paddle.extents = lvlwidth/2 - f.edge2d.thickness;
paddle.lasership();
paddle.pos = [0,-lvlheight/2+paddle.height*4];
paddle.pos = [0,-lvlheight/2+50];
paddle.setgrow(3);
Player.players[0].control(paddle);
paddle.inputs.space();
Gamestate.spawnball = function() {
var bb = World.spawn(gameobjects['ball']);
var bb = prototypes.get_ur('ball').make(self);
bb.pos = bb.pos.add([0,-200]);
bb.velocity = [30,200];
bb.draw_layer = 3;
bb.tag = 'ball';
}
var powerups = {};
@ -145,7 +149,7 @@ powerups.catch = mk_pup(Color.Arkanoid.Powerups.green, function() {
});
powerups.slow = mk_pup(Color.Arkanoid.Powerups.orange, function() {
/* Slow all balls */
gameobjects['ball'].instances.forEach(function(x) {
prototypes.get_ur('ball').instances.forEach(function(x) {
x.velocity = x.velocity.scale(0.5);
});
});
@ -153,7 +157,7 @@ powerups.break = mk_pup(Color.Arkanoid.Powerups.purple, function() {
clear_level();
}, 0.5);
powerups.disruption = mk_pup(Color.Arkanoid.Powerups.cyan, function() {
var ball = gameobjects['ball'].instances[0];
var ball = prototypes.get_ur('ball').instances[0];
if (!ball) return;
ball.instandup();
@ -207,8 +211,8 @@ var last_powerup = {};
function spawn_random_powerup(pos)
{
if (gameobjects['upgrade_drop'].instances.length !== 0) return;
var p = World.spawn(gameobjects['upgrade_drop']);
if (prototypes.get_ur('upgrade_drop').instances.length !== 0) return;
var p = prototypes.get_ur('upgrade_drop').make(self);
p.pos = pos;
var power = select_weighted(powerups.array());
@ -222,7 +226,7 @@ function spawn_random_powerup(pos)
}
function spawn_powerup(pos, fn) {
var p = World.spawn(gameobjects['upgrade_drop']);
var p = prototypes.get_ur('upgrade_drop').make(self);
p.pos = pos;
p.upgrade = fn;
return p;
@ -235,7 +239,7 @@ function multiball(n) {
function lostball()
{
if (gameobjects['ball'].instances.length === 0)
if (prototypes.get_ur('ball').instances.length === 0)
lostlife();
};
@ -255,7 +259,7 @@ function lostlife() {
};
Gamestate.spawnball();
var killbox = World.spawn(gameobjects['polygon2d']);
var killbox = prototypes.get_ur('polygon2d').make(self);
killbox.polygon2d.points = Geometry.box(lvlwidth,30);
killbox.pos = [0,-lvlheight/2];
@ -268,6 +272,8 @@ killbox.register_hit(hit => {
}, killbox);
killbox.polygon2d.sync();
//Sound.play("start.wav");
var pause = {
@ -278,7 +284,7 @@ var pause = {
},
unpause() {
unregister_gui(pause.gui);
Register.gui.unregister(pause.gui);
sim_start();
},
@ -292,7 +298,7 @@ var pause = {
var gamepawn =
{
pause() {
register_gui(pause.gui);
Register.gui.register(pause.gui);
sim_pause();
},
@ -310,7 +316,7 @@ var gamepawn =
}
function clear_level() {
var bricks = gameobjects['brick'].instances.slice();
var bricks = prototypes.get_ur('brick').instances.slice();
bricks.forEach(function(x) { x.kill(); });
};
@ -321,8 +327,8 @@ function gamegui() {
GUI.text_fn(`${score}`),
GUI.text_fn("HIGH SCORE", {color: Color.red}),
GUI.text_fn(`${highscore}`),
GUI.text_fn(`BRICKS: ${gameobjects['brick'].instances.length}`),
GUI.text_fn(`BALLS IN PLAY: ${gameobjects['ball'].instances.length}`),
GUI.text_fn(`BRICKS: ${prototypes.get_ur('brick').instances.length}`),
GUI.text_fn(`BALLS IN PLAY: ${prototypes.get_ur('ball').instances.length}`),
GUI.text_fn(`LIVES: ${lives}`),
GUI.text_fn("YARKANOID", {color: [5,120,240,255]}),
@ -333,7 +339,7 @@ function gamegui() {
}).draw(Window.dimensions.scale([0.8,0.8]));
}
register_gui(gamegui);
Register.gui.register(gamegui, this);
Player.players[0].control(gamepawn);
@ -341,6 +347,7 @@ let backtomain = function()
{
World.clear();
run("startmenu.js");
unregister_gui(gamegui);
Register.gui.unregister(gamegui);
Player.players[0].uncontrol(gamepawn);
}

35
brick.js Normal file
View file

@ -0,0 +1,35 @@
Object.assign(self, {
char: char2d.clone({
flash:Resources.load("brick.png"),
}),
points: 50,
getshot() {
this.kill();
},
start() {
this.char.stop();
this.flash();
this.char.color = Color.Rainbow.red;
},
flash() {
this.char.play("flash");
},
collide(hit) {
if (hit.obj.tag !== 'ball')
return;
this.powerup?.();
this.kill();
},
});
self.collider = polygon2d.clone();
self.collider.points = [[-8,4]];
self.collider.flipx = true;
self.collider.flipy = true;
self.char.pos = [-0.5, -0.5];

View file

@ -8,3 +8,5 @@
- Specifically, want to monitor for screen change
- Look up collision functions dynamically instead of with registry
-Missiles flash for one frame where previous

BIN
bump.wav Normal file

Binary file not shown.

186
config.js
View file

@ -1,17 +1,5 @@
gameobject.elasticity = 1;
gameobject.clone("ball", {
collider: circle2d.clone(),
img: sprite.clone(),
phys: 0,
elasticity: 1,
friction: 0,
collide(hit) {
Sound.play("bump.wav");
},
});
Color.Gameboy = {
darkest: [15,56,15],
dark: [48,98,48],
@ -27,177 +15,3 @@ Color.Rainbow = {
green: [0,153,1],
blue: [53,51,255]
};
gameobject.clone("brick", {
collider: polygon2d.clone(),
char: char2d.clone({
flash:Resources.load("brick.png"),
}),
points: 50,
start() {
this.char.stop();
this.flash();
this.char.color = Color.Rainbow.red;
},
flash() {
this.char.play("flash");
},
collide(hit) {
if (hit.obj.from !== 'ball')
return;
this.powerup?.();
this.kill();
},
});
var act_x = Action.add_new("move");
act_x.inputs.push("axis_ljoy");
var paddle = gameobject.clone("paddle", {
collider: polygon2d.clone(),
img: sprite.clone(),
phys: 1,
elasticity: 1,
speed: 1000,
length: 50,
height: 28,
lengths: [16,24,32,40,48],
growlvl: 1,
maxgrow: 5,
max_x: 0,
extents: 300,
input_larrow_down() { this.input_a_down(); },
frame_vel: [0,0],
input_a_down() {
this.frame_vel = this.frame_vel.add([-1,0]);
},
input_d_down() {
this.frame_vel = this.frame_vel.add([1,0]);
},
update(dt) {
this.angle = 0;
var fpos = this.pos;
fpos.x += this.frame_vel.x * this.speed * dt;
fpos.x = Math.clamp(fpos.x,-this.max_x,this.max_x);
this.pos = fpos;
this.frame_vel = [0,0];
if (this.stickball)
this.stickball.pos = this.pos.add(this.stickpos);
},
physupdate(dt) { this.velocity = [0,0]; },
gamepad_ljoy_axis(v) {
v[1] = 0;
this.pos = this.pos.add(v.scale(this.speed*Yugine.dt));
},
setup() {
Player.players[0].control(this);
this.length = this.lengths[0];
},
grow() {
this.setgrow(this.growlvl + 1);
},
setgrow(n) {
this.growlvl = Math.clamp(n,1,this.maxgrow);
this.length = this.lengths[this.growlvl];
this.img.path = "pill" + this.growlvl + ".png";
this.img.sync();
var d = this.img.dimensions;
this.collider.points = Geometry.box(d.x, d.y);
this.collider.sync();
this.max_x = this.extents - (d.x*this.scale)/2;
},
lasership() {
this.laser = true;
},
shoot() {
Log.warn("SHOOTING MISSILES");
},
sticky: false,
stored_vel: [0,0],
stickball: {},
stickpos: [0,0],
input_space_pressed() {
if (this.sticky && this.stickball) {
this.stickball.velocity = this.stored_vel;
this.stickball = {};
}
if (this.laser) {
this.shoot();
}
},
collide(hit) {
if (!(hit.obj.from === 'ball')) return;
var xdiff = hit.pos.x - this.pos.x;
var hitangle = xdiff/this.length - 0.5;
var speed = Vector.length(hit.obj.velocity);
hit.obj.velocity = [Math.cos(hitangle)*speed, Math.sin(hitangle)*speed];
hit.obj.velocity = hit.obj.velocity.scale(-1);
var oldvel = hit.obj.velocity;
var scaler = Math.max(Math.abs(xdiff)/30, 1.01);
hit.obj.velocity = hit.obj.velocity.scale(scaler);
if (this.sticky) {
this.stored_vel = hit.obj.velocity;
hit.obj.velocity = [0,0];
this.stickball = hit.obj;
this.stickpos = hit.obj.pos.sub(this.pos);
}
},
});
gameobject.clone("upgrade_drop", {
fallspeed: 150,
tag: "ball",
upgrade: function() {},
size: 12,
phys:1,
collider: circle2d.clone(),
color: Color.Rainbow.blue,
start() {
this.collider.radius = this.size;
this.collider.sensor = true;
},
draw() {
Shape.circle(this.pos,this.size,this.color);
},
collide(hit) {
if (!(hit.obj.from === 'paddle'))
return;
this.upgrade();
this.kill();
},
update(dt) {
this.pos = this.pos.add([0,-this.fallspeed*dt]);
},
});

View file

@ -1 +1,5 @@
load("startmenu.js");
Window.icon("powerup.png");
gameobject.scale = 2;
//World.load(prototypes.get_ur("startmenu"));
World.run("startmenu.js");

29
missile.js Normal file
View file

@ -0,0 +1,29 @@
var def = {
collider: polygon2d.clone(),
phys: 1,
bwidth: 5,
bheight: 15,
speed: 200,
start() {
this.collider.points = Geometry.box(this.bwidth, this.bheight);
this.collider.sensor = true;
},
collide(hit) {
if ('getshot' in hit.obj) {
hit.obj.getshot();
this.kill();
}
},
draw() {
Debug.box(this.pos, [this.bwidth, this.bheight], Color.cyan);
},
update(dt) {
this.pos = this.pos.add([0,this.speed*dt]);
},
};
Object.assign(self, def);

125
paddle.js Normal file
View file

@ -0,0 +1,125 @@
Object.assign(self, {
collider: polygon2d.clone(),
img: sprite.clone(),
phys: 1,
elasticity: 1,
tag: 'paddle',
speed: 1000,
length: 50,
height: 28,
lengths: [16,24,32,40,48],
growlvl: 1,
maxgrow: 5,
max_x: 0,
extents: 300,
frame_vel: [0,0],
update(dt) {
this.angle = 0;
var fpos = this.pos;
fpos.x += this.frame_vel.x * this.speed * dt;
fpos.x = Math.clamp(fpos.x,-this.max_x,this.max_x);
this.pos = fpos;
this.frame_vel = [0,0];
},
physupdate(dt) { this.velocity = [0,0]; },
gamepad_ljoy_axis(v) {
v[1] = 0;
this.pos = this.pos.add(v.scale(this.speed*Yugine.dt));
},
setup() {
this.length = this.lengths[0];
},
grow() {
this.setgrow(this.growlvl + 1);
},
setgrow(n) {
this.growlvl = Math.clamp(n,1,this.maxgrow);
this.length = this.lengths[this.growlvl];
this.img.path = "pill" + this.growlvl + ".png";
this.img.sync();
var d = this.img.dimensions;
this.collider.points = Geometry.box(d.x, d.y);
this.collider.sync();
this.max_x = this.extents - (d.x*this.scale)/2;
},
lasership() {
this.laser = true;
this.img.color = Color.Gameboy.light;
},
unlasership() {
this.laser = false;
this.img.color = Color.white;
},
shoot() {
var m1 = World.spawn(prototypes.get_ur("missile"));
var m2 = m1.dup();
m1.pos = this.pos.add([(this.length/2)-5,0]);
m2.pos = this.pos.add([5-(this.length/2),0]);
},
sticky: false,
stored_vel: [0,0],
start() {
this.collider.sensor = true;
},
collide(hit) {
if (!(hit.obj.tag === 'ball')) return;
var xdiff = hit.pos.x - this.pos.x;
var hitangle = (xdiff/this.length + 0.5) * Math.PI;
hitangle = Math.clamp(hitangle, Math.PI/6, 5*Math.PI/6);
var speed = Vector.length(hit.obj.velocity);
hit.obj.velocity = [Math.cos(hitangle)*speed, -Math.sin(hitangle)*speed];
hit.obj.velocity = hit.obj.velocity.scale(-1);
var oldvel = hit.obj.velocity;
var scaler = Math.max(Math.abs(xdiff)/30, 1.01);
hit.obj.velocity = hit.obj.velocity.scale(scaler);
if (this.sticky) {
this.stored_vel = hit.obj.velocity;
hit.obj.velocity = [0,0];
hit.obj.reparent(this);
this.stickball = hit.obj;
}
},
});
self.inputs = {};
self.inputs.space = function() {
if (this.sticky && this.stickball) {
this.stickball.velocity = this.stored_vel;
this.stickball.reparent(World);
delete this.stickball;
}
if (this.laser) {
this.shoot();
}
};
self.inputs.a = function(){};
self.inputs.a.down = function() {
this.frame_vel = this.frame_vel.add([-1,0]);
};
self.inputs.d = {};
self.inputs.d.down = function() {
this.frame_vel = this.frame_vel.add([1,0]);
};
self.img.pos = sprite.POS_MID;

BIN
powerup.wav Normal file

Binary file not shown.

View file

@ -26,11 +26,11 @@
"ball": {
"mass": 0.00001,
"collider": {
"radius": 4.39338754070273,
"ofset": [
0,
0
]
],
"radius": 2
},
"img": {
"pos": [
@ -104,7 +104,7 @@
"pos": [ -0.5, -0.5 ]
},
"angle": 0,
"scale": 2.91,
"scale": 2,
"from": "gameobject"
}
}

5
readme.md Normal file
View file

@ -0,0 +1,5 @@
# Breakout
Breakout, implemented with the Yugine. Classic Arkanoid style gameplay!
To play, just put the yugine executable the home directory and run.

BIN
select.wav Normal file

Binary file not shown.

BIN
start.wav Normal file

Binary file not shown.

View file

@ -1,13 +1,16 @@
var startgame = function()
{
self.kill();
run("breakout.js");
Player.players[0].uncontrol(startcontroller);
unregister_gui(startmenu);
// Player.players[0].uncontrol(startcontroller);
//Register.gui.unregister(startmenu,this);
// Register.gui.unregister_obj(this);
};
var exitgame = function()
{
quit();
Game.quit();
};
var colorshifter = {
@ -25,7 +28,6 @@ var options = GUI.column({
GUI.text_fn("START", {action: startgame}),
GUI.text_fn("EXIT", {action: exitgame}),
],
hovered: {},
});
Tween.embed(options, 'hovered', [colorshifter, colorshiftend], {time: 0.35, loop: "yoyo", whole: false, ease: Ease.sine.out});
@ -68,33 +70,28 @@ function startmenu() {
options.draw(Window.dimensions.scale([0.5,0.5]));
// GUI.image_fn({path:"arrow.png", anchor: [1,0.5]}).draw([item.bb.l, (item.bb.b + item.bb.t)/2].add([-3,1]));
GUI.image_fn({path:"coin.png", anchor: [1,0.5]}).draw([item.bb.l, (item.bb.b + item.bb.t)/2].add([-3,1]));
}
register_gui(startmenu);
Register.gui.register(startmenu, this);
var startcontroller = {
input_s_pressed() {
var startcontroller = {};
startcontroller.inputs = {};
startcontroller.inputs.s = function() {
item.selected = false;
idx = Math.clamp(idx+1, 0, options.items.length-1);
item = options.items[idx];
item.selected = true;
},
};
input_w_pressed() {
startcontroller.inputs.w = function() {
item.selected = false;
idx = Math.clamp(idx-1, 0, options.items.length-1);
item = options.items[idx];
item.selected = true;
},
input_enter_pressed() {
item.action();
},
input_escape_pressed() {
quit();
},
};
startcontroller.inputs.enter = function() { item.action(); };
Player.players[0].control(startcontroller);
Game.play();

BIN
ting.wav Normal file

Binary file not shown.

31
upgrade_drop.js Normal file
View file

@ -0,0 +1,31 @@
Object.assign(self, {
fallspeed: 150,
tag: "ball",
upgrade: function() {},
size: 12,
phys:1,
collider: circle2d.clone(),
color: Color.Rainbow.blue,
tag: 'powerup',
start() {
this.collider.radius = this.size;
this.collider.sensor = true;
},
draw() {
Shape.circle(this.pos,this.size,this.color);
},
collide(hit) {
if (!(hit.obj.tag === 'paddle'))
return;
this.upgrade();
this.kill();
},
update(dt) {
this.pos = this.pos.add([0,-this.fallspeed*dt]);
},
});