Skip to content

Commit

Permalink
*** Finished top-down shooter demo
Browse files Browse the repository at this point in the history
* Finished implementing top-down shooter demo, adding a lot of functionality and assets
* Added Vector.projectOnto() and Vector.rotate()
dubrowgn committed Apr 24, 2013
1 parent 0c2bcd8 commit 8be0c31
Showing 19 changed files with 1,034 additions and 202 deletions.
Binary file added example/shooter/assets/image/dirt_splat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified example/shooter/assets/image/grass.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/health.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed example/shooter/assets/image/map.jpg
Binary file not shown.
File renamed without changes
Binary file added example/shooter/assets/image/player_brown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/player_green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/player_purple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/shot_blue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/shot_green.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/shot_purple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/shooter/assets/image/shot_red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
178 changes: 178 additions & 0 deletions example/shooter/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"use strict";

var config = {
host:"0.0.0.0",
port: 1400
};

var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer(config);
var clients = [];
var clientID = 1;
var entities = [];

function log(text) {
var now = new Date();
console.log("[%s %s] %s", now.toDateString(), now.toLocaleTimeString(), text);
} // log( )

var commandId = {
localConnect:0,
localDisconnect:1,
remoteConnect:2,
remoteDisconnect:3,
measurePing:10,
addEntity:20,
faceEntity:21,
removeEntity:22,
moveEntity:23,
playerDied:24
};

function onConnection(ws) {
ws.clientID = clientID++;
clients[ws.clientID] = ws;
log("connection opened (clientID " + ws.clientID + ")");

var send = ws.send.bind(ws);
ws.send = function(msg) {
log(ws.clientID + " <-- " + msg);
if (ws.readyState === 1)
send(msg);
};

var propagate = function(msg) {
for (var cid = 0; cid < clients.length; ++cid) {
var client = clients[cid];
if (client !== undefined && client != ws)
client.send(msg);
} // for( cid )
};

ws.on("close", function() {
log("connection closed (clientID " + ws.clientID + ")");

var client = clients[ws.clientID];
clients[ws.clientID] = undefined;

if (client !== undefined) {
// remove player entity from game world
entities[ws.clientID * 100000] = undefined;
propagate(commandId.removeEntity + " " + (ws.clientID * 100000));

// send remote disconnect to all other connected clients
propagate(commandId.remoteDisconnect + " " + ws.clientID);
} // if
});
ws.on('error', function(e) {
log("Error (clientID " + ws.clientID + "): " + e.message);
});
ws.on("message", function(message) {
log(ws.clientID + " --> " + message);

var tokens = message.split(" ");
var cmd = parseInt(tokens[0]);

switch(cmd) {
case commandId.localConnect: // cid
// return local connect request with new clientID
ws.send(commandId.localConnect + " " + ws.clientID);

// send remote connect command to all other connected clients
propagate(commandId.remoteConnect + " " + ws.clientID);

break;
case commandId.localDisconnect: // ...
// release connection for requesting client
clients[ws.clientID] = undefined;
ws.send(commandId.localDisconnect + "");

// remove player entity from game world
entities[ws.clientID * 100000] = undefined;
propagate(commandId.removeEntity + " " + (ws.clientID * 100000));

// send remote disconnect to all other connected clients
propagate(commandId.remoteDisconnect + " " + ws.clientID);

break;
case commandId.measurePing: // ...
// return measure ping request
ws.send(commandId.measurePing + "");

break;
case commandId.addEntity: // cid, eid, etid, x, y, dx, dy
// create new entity object cache
entities[parseInt(tokens[2])] = {
cid: parseInt(tokens[1]),
eid: parseInt(tokens[2]),
etid: parseInt(tokens[3]),
x: parseFloat(tokens[4]),
y: parseFloat(tokens[5]),
dx: parseFloat(tokens[6]),
dy: parseFloat(tokens[7]),
lastUpdated: new Date() | 0
};

// propagate message to all other connected clients
propagate(message);

break;
case commandId.faceEntity: // eid, face
// propagate message to all other connected clients
propagate(message);

break;
case commandId.removeEntity: // eid
// remove entity from server cache
entities[parseInt(tokens[1])] = undefined;

// propagate message to all other connected clients
propagate(message);

break;
case commandId.moveEntity: // eid, x, y, dx, dy
// update entity server cache
var ent = entities[parseInt(tokens[1])];
if (ent !== undefined) {
ent.x = parseFloat(tokens[2]);
ent.y = parseFloat(tokens[3]);
ent.dx = parseFloat(tokens[4]);
ent.dy = parseFloat(tokens[5]);
ent.lastUpdated = new Date() | 0;
} // if

// propagate message to all other connected clients
propagate(message);

break;
case commandId.playerDied: // cidDied, cidKilled
// propagate message to all other connected clients
propagate(message);

break;
default: // ...
log("Received invalid message from client " + ws._clientId + ": " + message);
break;
} // switch
});

// sync existing clients back to newly connected client
for (var cid = 0; cid < clients.length; ++cid) {
if (clients[cid] !== undefined)
ws.send(commandId.remoteConnect + " " + cid);
} // for( i )

// sync existing entities back to newly connected client
for (var eid = 0; eid < entities.length; ++eid) {
var ent = entities[eid];
if (ent !== undefined) {
//cid, eid, etid, x, y, dx, dy
var dt = (new Date() - ent.lastUpdated) / 1000;
ws.send(commandId.addEntity + " " + ent.cid + " " + ent.eid + " " + ent.etid + " " + (ent.x + ent.dx * dt).toFixed(3) + " " + (ent.y + ent.dy * dt).toFixed(3) + " " + ent.dx.toFixed(3) + " " + ent.dy.toFixed(3));
} // if
} // for( i )
} // onConnection( )

wss.on("connection", onConnection);

log("Server started on " + config.host + ":" + config.port);
109 changes: 30 additions & 79 deletions example/shooter/shooter.html
Original file line number Diff line number Diff line change
@@ -6,80 +6,35 @@
<script type="text/javascript" src="../../impulse.js" charset="utf-8"></script>
<script type="text/javascript" src="shooter.js" charset="utf-8"></script>
<script type="text/javascript">
var vpHeight = 432;
var vpWidth = 768;
var canvas;
var ui_btns;
var ui_status_deaths;
var ui_status_fps;
var ui_status_lives;
var ui_status_money;
var ui_status_version;
var ui_status_wave;
var ui_status_health;
var ui_status_kills;
var ui_status_ping;
var shooter;

function Start() {
canvas = document.getElementById("canvas");
ui_status_deaths = document.getElementById("ui_status_deaths");
ui_status_fps = document.getElementById("ui_status_fps");
ui_status_health = document.getElementById("ui_status_health");
ui_status_kills = document.getElementById("ui_status_kills");
ui_status_ping = document.getElementById("ui_status_ping");

window.addEventListener("resize", OnResize);
OnResize();

var shooter = new Shooter(canvas);
shooter = new Shooter(canvas);

shooter.deathsChanged.add(function(deaths) { ui_status_deaths.innerHTML = "Deaths: " + deaths; });
shooter.fpsChanged.add(function(fps) { ui_status_fps.innerHTML = "FPS: " + (fps | 0); });
shooter.healthChanged.add(function(current, max) { ui_status_health.style.width = (current / max) * 100 + "%"; });
shooter.killsChanged.add(function(kills) { ui_status_kills.innerHTML = "Kills: " + kills; });
shooter.pingChanged.add(function(ping) { ui_status_ping.innerHTML = "Ping: " + ping.toFixed(2) + "ms"; });

// ui_btns = document.getElementById("ui_btns");
// ui_status_fps = document.getElementById("ui_status_fps");
// ui_status_lives = document.getElementById("ui_status_lives");
// ui_status_money = document.getElementById("ui_status_money");
// ui_status_version = document.getElementById("ui_status_version");
// ui_status_wave = document.getElementById("ui_status_wave");
//
// window.onresize = OnResize;
// OnResize();
// CanvasTD.Setup(canvas, vpWidth, vpHeight);
//
// // init button and their event handlers
// var btn;
// btn = UIFactory.CreateButton("img/btnTowers.png", 32, 32, 0);
// btn.onclick = CanvasTD.BuildTowerGreen;
// ui_btns.appendChild(btn);
// btn = UIFactory.CreateButton("img/btnTowers.png", 32, 32, 32);
// btn.onclick = CanvasTD.BuildTowerBlue;
// ui_btns.appendChild(btn);
// btn = UIFactory.CreateButton("img/btnTowers.png", 32, 32, 64);
// btn.onclick = CanvasTD.BuildTowerRed;
// ui_btns.appendChild(btn);
// btn = UIFactory.CreateButton("img/btnTowers.png", 32, 32, 96);
// btn.onclick = CanvasTD.BuildTowerBlack;
// ui_btns.appendChild(btn);
// btn = UIFactory.CreateButton("img/btnTowers.png", 32, 32, 128);
// btn.onclick = CanvasTD.BuildTowerWhite;
// ui_btns.appendChild(btn);
//
// // init keyboard event handlers
// key("space", CanvasTD.TogglePause);
// key("escape", CanvasTD.CancelBuild);
// key("q", CanvasTD.BuildTowerGreen);
// key("w", CanvasTD.BuildTowerBlue);
// key("e", CanvasTD.BuildTowerRed);
// key("r", CanvasTD.BuildTowerBlack);
// key("t", CanvasTD.BuildTowerWhite);
//
// // init status event handlers
// CanvasTD.FpsChanged = function(_fps) {
// ui_status_fps.innerHTML = "FPS: " + _fps.toFixed(2);
// };
// CanvasTD.LivesChanged = function(_lives) {
// ui_status_lives.innerHTML = _lives + " Lives";
// };
// CanvasTD.MoneyChanged = function(_money) {
// ui_status_money.innerHTML = "$" + _money.toFixed(2);
// };
// CanvasTD.WaveChanged = function(_wave) {
// ui_status_wave.innerHTML = "Wave: " + _wave;
// };
//
// // init static information
// ui_status_version.innerHTML = CanvasTD.version;
shooter.connected.add(function() { shooter.run(); });
shooter.connect();
} // start( )

function OnResize() {
@@ -90,29 +45,25 @@
<style type="text/css">
canvas { position:absolute; }
body { margin:0; }
#ui_bg { background:url("img/ui_dark.png") repeat-x; position:absolute; width:100%; height:48px; bottom:0px; }
#ui_btns { text-align:center; position:absolute; width:100%; bottom:0px; }
#ui_status_top { position:absolute; top:0px; font:9pt helvetica; font-weight:bold; padding:2px; }
#ui_status_version { color:#fff; }
#ui_status_fps { color:#fff; }
#ui_status_bottom { position:absolute; bottom:0px; font:9pt helvetica; font-weight:bold; padding:2px; }
#ui_status_money { color:#ffff00; }
#ui_status_lives { color:#ff0000; }
#ui_status_wave { color:#000000; }
#ui_status_top { position:absolute; top:0; font:12pt helvetica; font-weight:bold; padding:2px; color:#fff; text-shadow: 1px 1px 4px #000; }
#ui_status_health {
width:100%;
height:24px;
background:url("assets/image/health.png");
position:absolute;;
bottom:0;
}
#ui_status_ping { color:#fff; }
</style>
</head>
<body onload="Start();">
<canvas id="canvas"></canvas>
<div id="ui_status_top">
<!--div id="ui_status_version"></div-->
<div id="ui_status_fps">FPS: &mdash;</div>
<div id="ui_status_ping">Ping: &mdash;</div>
<div id="ui_status_kills">Kills: 0</div>
<div id="ui_status_deaths">Deaths: 0</div>
</div>
<!--div id="ui_bg"></div>
<div id="ui_btns"></div>
<div id="ui_status_bottom">
<div id="ui_status_money">$40.00</div>
<div id="ui_status_lives">50 Lives</div>
<div id="ui_status_wave">Wave: 1</div>
</div-->
<div id="ui_status_health"></div>
</body>
</html>
Loading

0 comments on commit 8be0c31

Please sign in to comment.