Some 2D Space Shooter

Greetings,

I realized that with PacMan being copyright, it’s a bit tough to share to showcase the potential for making games in SuperCollider.

Here’s another crack at making some 2D games I started today. This one is the beginning of a top down, 2D outer space shooter that has no enemies or goals yet. Note - I have absolutely no idea what the unicode for space is on Linux or Windows. 20 was my best guess for Windows, but this works*** on a Mac.

Enjoy and feel free to reuse the code for whatever.

***with some obvious bugs in how the laser beams behave :joy:

(
s = Server.local;
s.waitForBoot({
var title = "SuperSpaceCollider";
var winW = 480, winH = 640;
var win = Window.new(title, Rect(300,100,winW,winH), false)
.front
.onClose_({Window.closeAll});

var keycodes = Platform.case(
	//<-, ->, ↓, ↑
	\osx, {[123,124,125,126,49]},
	\linux, {[65361,65363,65364,65362]},
	\windows, {[37,39,40,38,20]}
);
	
var numLasers = 0;

var movementSpeed = 3;

var playerDir = Set(1);

var firing = false;

//colors
var
brown = Color.new255(216, 145, 85),
pink = Color.new255(252, 180, 255),
red = Color.new255(252, 0, 0),
salmon = Color.new255(252, 180, 170),
orange = Color.new255(252, 180, 85),
yellow = Color.new255(252, 252, 0),
green = Color.new255(0, 252, 0),
teal = Color.new255(72, 180, 170),
cyan = Color.new255(0, 252, 255),
blue = Color.new255(72, 180, 255),
indigo = Color.new255(36, 36, 255),
white = Color.white,
black = Color.black,
clear = Color.clear;

var spriteH=35, spriteW=35;

var playerX=winW/2-spriteW, playerY=winH;
	
		var lasers = 0, laserX = playerX, laserY = playerY, shipScale=1;

var board = UserView(win, win.view.bounds)
	.background_(Color.blue(0.1));


var playerSprite = UserView(board, win.view.bounds)
.animate_(true)
.drawFunc_({
		var cockpitH = spriteH/3, cockpitW = spriteW/7;
		var numLines = 4;
	//movement
	playerDir.do({
		arg item;
	case
	{item == (\left)} {playerX=playerX-movementSpeed}
	{item == (\right)} {playerX=playerX+movementSpeed}
	{item == (\down)} {playerY=playerY+movementSpeed}
	{item == (\up)} {playerY=playerY-movementSpeed};
	});
	playerY=playerY.clip(0,win.view.bounds.height-spriteH);
	playerX=playerX.clip(spriteW.neg/2, win.view.bounds.width-(3/2*spriteW));
	Pen.translate(playerX+(spriteW/2), playerY);

	//draw
			
		Pen.moveTo(spriteW/2 @ 0).scale(shipScale, 1);
	//wings
	2.do({
		arg i;
			var ratio = (1/2);
		Pen
			.moveTo(spriteW/2 @ (7*spriteH/8))
			.lineTo(i*spriteW @ spriteH)
			.lineTo(i*spriteW @ (15*spriteH/16))
			.quadCurveTo(spriteW/2 @ 0, spriteW/2 @ (spriteH*ratio))
		.fillColor_(Color.grey(0.4))
		.strokeColor_(Color.grey(0.3))
		.draw(3);
	});
		
		//decor
	numLines.do({
		arg i;
		Pen
			.line(spriteW/numLines*(i+0.5) @ spriteH, spriteW/numLines*(i+0.5) @ (7*spriteH/10))
		.strokeColor_(Color.grey)
		.width_(2)
		.stroke
	});
		
			//decor
	2.do({
		arg i;
		Pen
			.line(i*spriteW @ spriteH, i*spriteW @ (6*spriteH/10))
		.strokeColor_(Color.grey)
		.width_(3)
		.stroke
	});
		
		//body
	2.do({
		arg i;
		Pen
		.moveTo((spriteW/2) @ 0)
		.quadCurveTo(spriteW/2 @ (spriteH), spriteW/4+(i*spriteW/2) @ (spriteH/2))
		.width_(1)
		.fillColor_(Color.grey(0.5))
		.strokeColor_(Color.grey)
		.draw(3)
	});
		
		//cockpit
		Pen.addOval(Rect(spriteW-cockpitW/2, (4*spriteH/10), cockpitW, cockpitH))
			.fillColor_(white)
		.strokeColor_(Color.grey(0.7))
		.draw(3);
		
		//propulsion
	5.rrand(12).do({
		arg i;
		Pen.color_([cyan,blue,indigo, white].choose)
		.line(17.5 @ (7*spriteH/8), 15.rrand(20) @ ((1*spriteH/2).rand + (7*spriteH/8)))
		.width_(3.rand)
		.stroke;
	});
		
})
.background_(clear);
	
	var laserView = UserView(playerSprite, win.view.bounds)
	.animate_(true)
	.drawFunc_({
		case 
		{firing == true}
		{laserX.do({
			arg item, i;
			laserY = laserY-4;
			Pen.line(item@laserY, item@(laserY+5)).width_(3).color_(red).stroke});
		};
	});

		//sounds
	var pow = {
		var num = 3.rrand(7);
		Pan2.ar(
		Mix.ar(
			num.collect({
			arg i;
		LFPulse.ar(100.rrand(200)*Line.ar(4,0.5,0.2), iphase:0.0, width:0.1.rrand(0.9)*SinOsc.ar(2, (pi/2).rand),
			mul:EnvGen.ar(Env.perc(0.001,0.5,curve:-4), doneAction:2))
		}))/num,
			playerX.linlin(0, winW, -1.0, 1.0))};

	View.globalKeyDownAction_({
		arg view, char, mod, uni, keycode;
		case
	{keycode == keycodes[0]} {playerDir.add(\left);}
		{keycode == keycodes[1]} {playerDir.add(\right);}
	{keycode == keycodes[2]} {playerDir.add(\down);}
	{keycode == keycodes[3]} {playerDir.add(\up);}
		{keycode == keycodes[4]} {pow.play; laserX = [playerX+(0.5*spriteW), playerX+(1.5*spriteW)]; laserY = playerY+(spriteH/2); firing = true; numLasers=numLasers+1}
		{uni == 27} {win.close; win.refresh};
	});
	View.globalKeyUpAction_({
		arg view, char, mod, uni, keycode;
		case
	{keycode == keycodes[0]} {playerDir.remove(\left);}
	{keycode == keycodes[1]} {playerDir.remove(\right);}
	{keycode == keycodes[2]} {playerDir.remove(\down);}
	{keycode == keycodes[3]} {playerDir.remove(\up);}
	{keycode == keycodes[4]} {numLasers=numLasers}
	});
		win.refresh;
});
)
4 Likes

This is great. The level of detail for the sprite and the laser sounds is much appreciated. I can’t wait to play the whole thing when it is “done”.

Sam

Just tried in Linux and Windows – space is 32 in both. (“20” sounds like hexadecimal – 0x20 = 32.)

It’s slick!

Also try making a SynthDef for “pow” instead of playing a function (better for timing, and efficiency).

hjh

Question James, how can I get playerY to set the value of Pan2.ar from a SynthDef? Would I just call Synth.set from drawFunc_({})? And I was also really unsure about iterating with rrand in a SynthDef… This isn’t possible right? I agree about the SynthDef though, I really should just pick a number and call it a day probably.

Thanks in advance,
JS

EDIT: Never mind, I got it :slight_smile:

Hi Mjsyts,

this is great and I love seeing someone interested in making games! Me too.

I’ve been writing a game engine in Supercollider for the last 2 years and I’m getting close to putting something out. Maybe this is something that might help you.

neil : )

4 Likes

Absolutely! Are you on GitHub? I’ve found this to be a lot of fun. I was really uncomfortable with GUI when I started and I was getting tired of making excuses about it.

Yes the project is on GIT hub but its private at the moment.
Let me check I haven’t got any copyrighted assets in it before I go public.

1 Like

The first playable version is complete! Updates will likely be forthcoming to make it more difficult and fine tune a few game mechanics, but I am thrilled at how well it runs so far.

4 Likes

Amazing work!! Thanks for sharing!

Hey mjsyts,
yeah this is great! :slight_smile: I love your hand crafted vector work, I appreciate how long that must have taken. Could I steal your ship and use as examples in my game engine? It’s almost done, a couple more weeks and I’m going to put it out in the world.
I’ll credit you if you’re ok with it.
Neil

Go for it! Thanks for noticing the vector work and for asking. That ship was a labor of love.

1 Like

:slight_smile:

1 Like

Oh wow this is awesome! Thanks so much!