Move enemy/scoreboard/startbutton in shortbow to actor model

This commit is contained in:
Ryan Huffman 2017-02-01 10:14:19 -08:00
parent 582b1eca46
commit ff71d5df97
5 changed files with 258 additions and 65 deletions

View file

@ -0,0 +1,33 @@
(function() {
Script.include('utils.js');
Enemy = function() {
};
Enemy.prototype = {
preload: function(entityID) {
print("Loaded enemy entity");
this.entityID = entityID;
var userData = Entities.getEntityProperties(this.entityID, 'userData').userData;
var data = utils.parseJSON(userData);
if (data !== undefined && data.gameChannel !== undefined) {
this.gameChannel = data.gameChannel;
} else {
print("enemyServerEntity.js | ERROR: userData does not contain a game channel and/or team number");
}
var self = this;
this.heartbeatTimerID = Script.setInterval(function() {
print("Sending heartbeat", self.gameChannel);
Messages.sendMessage(self.gameChannel, JSON.stringify({
type: "enemy-heartbeat",
entityID: self.entityID,
position: Entities.getEntityProperties(self.entityID, 'position').position
}));
}, 1000);
},
unload: function() {
Script.clearInterval(this.heartbeatTimerID);
},
};
return new Enemy();
});

View file

@ -102,6 +102,7 @@ function createLocalGame() {
buttonID = spawnTemplate("SB.StartButton", { buttonID = spawnTemplate("SB.StartButton", {
parentID: scoreboardID, parentID: scoreboardID,
script: Script.resolvePath("startGameButton.js"), script: Script.resolvePath("startGameButton.js"),
serverScripts: Script.resolvePath("startGameButtonServerEntity.js"),
userData: JSON.stringify({ userData: JSON.stringify({
grabbableKey: { grabbableKey: {
wantsTrigger: true wantsTrigger: true
@ -121,25 +122,37 @@ function createLocalGame() {
waveDisplayID = spawnTemplate("SB.DisplayWave", { waveDisplayID = spawnTemplate("SB.DisplayWave", {
parentID: scoreboardID, parentID: scoreboardID,
serverScripts: "null" userData: JSON.stringify({
displayType: "wave"
}),
serverScripts: Script.resolvePath("scoreboardEntity.js")
}); });
entityIDs.push(waveDisplayID); entityIDs.push(waveDisplayID);
scoreDisplayID = spawnTemplate("SB.DisplayScore", { scoreDisplayID = spawnTemplate("SB.DisplayScore", {
parentID: scoreboardID, parentID: scoreboardID,
serverScripts: "null" userData: JSON.stringify({
displayType: "score"
}),
serverScripts: Script.resolvePath("scoreboardEntity.js")
}); });
entityIDs.push(scoreDisplayID); entityIDs.push(scoreDisplayID);
livesDisplayID = spawnTemplate("SB.DisplayLives", { livesDisplayID = spawnTemplate("SB.DisplayLives", {
parentID: scoreboardID, parentID: scoreboardID,
serverScripts: "null" userData: JSON.stringify({
displayType: "lives"
}),
serverScripts: Script.resolvePath("scoreboardEntity.js")
}); });
entityIDs.push(livesDisplayID); entityIDs.push(livesDisplayID);
highScoreDisplayID = spawnTemplate("SB.DisplayHighScore", { highScoreDisplayID = spawnTemplate("SB.DisplayHighScore", {
parentID: scoreboardID, parentID: scoreboardID,
serverScripts: "null" userData: JSON.stringify({
displayType: "highscore"
}),
serverScripts: Script.resolvePath("scoreboardEntity.js")
}); });
entityIDs.push(highScoreDisplayID); entityIDs.push(highScoreDisplayID);

View file

@ -0,0 +1,37 @@
(function() {
Script.include('utils.js');
Display = function() {
};
Display.prototype = {
preload: function(entityID) {
this.entityID = entityID;
this.entityIDsThatHaveCollidedWithMe = [];
var props = Entities.getEntityProperties(this.entityID, ['userData', 'parentID']);
var data = utils.parseJSON(props.userData);
if (data !== undefined && data.displayType !== undefined) {
this.gameChannel = data.displayType + "-" + props.parentID;
} else {
print("scoreboardEntity.js | ERROR: userData does not contain a game channel and/or team number");
}
Messages.subscribe(this.gameChannel);
Messages.messageReceived.connect(this, this.onReceivedMessage);
},
unload: function() {
Messages.unsubscribe(this.gameChannel);
Messages.messageReceived.disconnect(this, this.onReceivedMessage);
},
onReceivedMessage: function(channel, message, senderID) {
if (channel === this.gameChannel) {
Entities.editEntity(this.entityID, {
text: message
});
}
},
};
return new Display();
});

View file

@ -37,7 +37,7 @@ function encodeURLParams(params) {
return paramPairs.join("&"); return paramPairs.join("&");
} }
function sendAndUpdateHighScore(highScoreDisplayID, entityID, score, wave, numPlayers) { function sendAndUpdateHighScore(highScoreChannel, entityID, score, wave, numPlayers) {
const URL = 'https://script.google.com/macros/s/AKfycbwbjCm9mGd1d5BzfAHmVT_XKmWyUYRkjCEqDOKm1368oM8nqWni/exec'; const URL = 'https://script.google.com/macros/s/AKfycbwbjCm9mGd1d5BzfAHmVT_XKmWyUYRkjCEqDOKm1368oM8nqWni/exec';
print("Sending high score"); print("Sending high score");
@ -55,9 +55,10 @@ function sendAndUpdateHighScore(highScoreDisplayID, entityID, score, wave, numPl
print("Got response for high score: ", req.response); print("Got response for high score: ", req.response);
var response = JSON.parse(req.responseText); var response = JSON.parse(req.responseText);
if (response.highScore !== undefined) { if (response.highScore !== undefined) {
Entities.editEntity(highScoreDisplayID, { Messages.sendMessage(highScoreChannel, response.highScore);
text: response.highScore //Entities.editEntity(highScoreDisplayID, {
}); //text: response.highScore
//});
} }
} }
}; };
@ -112,6 +113,7 @@ var baseEnemyProperties = {
"y": -15, "y": -15,
"z": 0 "z": 0
}, },
lifetime: 180,
"id": "{ed8f7339-8bbd-4750-968e-c3ceb9d64721}", "id": "{ed8f7339-8bbd-4750-968e-c3ceb9d64721}",
"modelURL": "http://hifi-content.s3.amazonaws.com/Examples%20Content/production/marblecollection/Amber.fbx?2", "modelURL": "http://hifi-content.s3.amazonaws.com/Examples%20Content/production/marblecollection/Amber.fbx?2",
"name": "SB.Enemy", "name": "SB.Enemy",
@ -137,11 +139,12 @@ var baseEnemyProperties = {
z: -3 z: -3
}, },
script: Script.resolvePath('enemyEntity.js'), script: Script.resolvePath('enemyEntity.js'),
serverScripts: "null" serverScripts: Script.resolvePath('enemyServerEntity.js')
}; };
ShortbowGameManager = function(rootPosition, gatePosition, bowPositions, spawnPositions, rootEntityID, startButtonID, waveDisplayID, scoreDisplayID, livesDisplayID, highScoreDisplayID) { ShortbowGameManager = function(rootPosition, gatePosition, bowPositions, spawnPositions, rootEntityID, startButtonID, waveDisplayID, scoreDisplayID, livesDisplayID, highScoreDisplayID) {
print("Starting game manager");
this.gameState = GAME_STATES.IDLE; this.gameState = GAME_STATES.IDLE;
this.bowPositions = bowPositions; this.bowPositions = bowPositions;
@ -149,12 +152,11 @@ ShortbowGameManager = function(rootPosition, gatePosition, bowPositions, spawnPo
this.spawnPositions = spawnPositions; this.spawnPositions = spawnPositions;
this.gatePosition = gatePosition; this.gatePosition = gatePosition;
this.rootEntityID = rootEntityID; this.rootEntityID = rootEntityID;
this.startButtonID = startButtonID; //this.startButtonID = startButtonID;
this.waveDisplayID = waveDisplayID; //this.waveDisplayID = waveDisplayID;
this.scoreDisplayID = scoreDisplayID; //this.scoreDisplayID = scoreDisplayID;
this.livesDisplayID = livesDisplayID; //this.livesDisplayID = livesDisplayID;
this.highScoreDisplayID = highScoreDisplayID; //this.highScoreDisplayID = highScoreDisplayID;
print(waveDisplayID, scoreDisplayID, livesDisplayID);
// Gameplay state // Gameplay state
this.waveNumber = 0; this.waveNumber = 0;
@ -162,24 +164,32 @@ ShortbowGameManager = function(rootPosition, gatePosition, bowPositions, spawnPo
this.score = 0; this.score = 0;
this.nextWaveTimer = null; this.nextWaveTimer = null;
this.spawnEnemyTimers = []; this.spawnEnemyTimers = [];
this.enemyIDs = []; //this.enemyIDs = [];
this.remainingEnemies = [];
this.entityIDs = []; this.entityIDs = [];
this.bowIDs = []; this.bowIDs = [];
this.commChannelName = "shortbow-" + this.rootEntityID; this.commChannelName = "shortbow-" + this.rootEntityID;
print("Listening on: ", this.commChannelName); print("Listening on: ", this.commChannelName);
//this.scoreChannelName = "score-" + this.rootEntityID;
this.highscoreChannelName = "highscore-" + this.rootEntityID;
//this.waveChannelName = "wave-" + this.rootEntityID;
//this.livesChannelName = "lives-" + this.rootEntityID;
this.startButtonChannelName = 'button-' + this.rootEntityID;
Messages.subscribe(this.commChannelName); Messages.subscribe(this.commChannelName);
Messages.messageReceived.connect(this, this.onReceivedMessage); Messages.messageReceived.connect(this, this.onReceivedMessage);
this.reset(); this.reset();
sendAndUpdateHighScore(this.highScoreDisplayID, this.rootEntityID, this.score, this.waveNumber, 1); sendAndUpdateHighScore(this.highscoreChannelName, this.rootEntityID, this.score, this.waveNumber, 1);
} }
ShortbowGameManager.prototype = { ShortbowGameManager.prototype = {
reset: function() { reset: function() {
Entities.editEntity(this.startButtonID, { //Entities.editEntity(this.startButtonID, {
visible: true //visible: true
}); //});
Messages.sendMessage(this.startButtonChannelName, 'show');
}, },
cleanup: function() { cleanup: function() {
Messages.unsubscribe(this.commChannelName); Messages.unsubscribe(this.commChannelName);
@ -193,10 +203,10 @@ ShortbowGameManager.prototype = {
Entities.deleteEntity(this.bowIDs[i]); Entities.deleteEntity(this.bowIDs[i]);
} }
this.bowIDs = []; this.bowIDs = [];
for (var i = 0; i < this.enemyIDs.length; i++) { for (var i = 0; i < this.remainingEnemies.length; i++) {
Entities.deleteEntity(this.enemyIDs[i]); Entities.deleteEntity(this.remainingEnemies[i].id);
} }
this.enemyIDs = []; this.remainingEnemies = [];
}, },
onReceivedMessage: function(channel, messageJSON, senderID) { onReceivedMessage: function(channel, messageJSON, senderID) {
print("playWaveGame.js | Recieved: " + messageJSON + " from " + senderID, channel, this.commChannelName); print("playWaveGame.js | Recieved: " + messageJSON + " from " + senderID, channel, this.commChannelName);
@ -204,7 +214,7 @@ ShortbowGameManager.prototype = {
if (channel === this.commChannelName) { if (channel === this.commChannelName) {
var message = utils.parseJSON(messageJSON); var message = utils.parseJSON(messageJSON);
if (message === undefined) { if (message === undefined) {
print("playWaveGame.js | Received non-json message"); print("playWaveGame.js | Received non-json message:", JSON.stringify(messageJSON));
return; return;
} }
switch (message.type) { switch (message.type) {
@ -217,6 +227,16 @@ ShortbowGameManager.prototype = {
case 'enemy-escaped': case 'enemy-escaped':
this.onEnemyEscaped(message.entityID); this.onEnemyEscaped(message.entityID);
break; break;
case 'enemy-heartbeat':
for (var i = 0; i < this.remainingEnemies.length; i++) {
var enemy = this.remainingEnemies[i];
if (enemy.id == message.entityID) {
enemy.lastHeartbeat = Date.now();
enemy.lastKnownPosition = message.position;
break;
}
}
break;
default: default:
print("playWaveGame.js | Ignoring unknown message type: ", message.type); print("playWaveGame.js | Ignoring unknown message type: ", message.type);
break; break;
@ -231,7 +251,8 @@ ShortbowGameManager.prototype = {
print("Game started!!"); print("Game started!!");
Entities.editEntity(this.startButtonID, { visible: false }); //Entities.editEntity(this.startButtonID, { visible: false });
Messages.sendMessage(this.startButtonChannelName, 'hide');
// Spawn bows // Spawn bows
@ -277,7 +298,8 @@ ShortbowGameManager.prototype = {
this.nextWaveTimer = Script.setTimeout(this.startNextWave.bind(this), 100); this.nextWaveTimer = Script.setTimeout(this.startNextWave.bind(this), 100);
this.spawnEnemyTimers = []; this.spawnEnemyTimers = [];
this.checkEnemyPositionsTimer = null; this.checkEnemyPositionsTimer = null;
this.enemyIDs = []; //this.enemyIDs = [];
this.remainingEnemies = [];
// SpawnQueue is a list of enemies left to spawn. Each entry looks like: // SpawnQueue is a list of enemies left to spawn. Each entry looks like:
// //
@ -294,18 +316,32 @@ ShortbowGameManager.prototype = {
position: this.rootPosition position: this.rootPosition
}); });
var self = this;
//Script.setInterval(function() {
//print("Remaining enemies:");
//for (var i = 0; i < self.remainingEnemies.length; ++i) {
//print(" ", i, self.remainingEnemies[i].id);
//}
//}, 3000);
},
setDisplayText: function(type, value) {
var channel = type + '-' + this.rootEntityID;
Messages.sendMessage(channel, value);
}, },
startNextWave: function() { startNextWave: function() {
print("Starting next wave"); print("Starting next wave");
this.gameState = GAME_STATES.PLAYING; this.gameState = GAME_STATES.PLAYING;
this.waveNumber++; this.waveNumber++;
this.enemyIDs = []; //this.enemyIDs = [];
this.remainingEnemies= [];
this.spawnQueue = []; this.spawnQueue = [];
this.spawnStartTime = Date.now(); this.spawnStartTime = Date.now();
Entities.editEntity(this.waveDisplayID, { //Entities.editEntity(this.waveDisplayID, {
text: this.waveNumber //text: this.waveNumber
}); //});
this.setDisplayText('wave', this.waveNumber);
var numberOfEnemiesLeftToSpawn = this.waveNumber * 2; var numberOfEnemiesLeftToSpawn = this.waveNumber * 2;
var delayBetweenSpawns = 2000 / Math.max(1, Math.log(this.waveNumber)); var delayBetweenSpawns = 2000 / Math.max(1, Math.log(this.waveNumber));
@ -329,7 +365,7 @@ ShortbowGameManager.prototype = {
return; return;
} }
if (this.spawnQueue.length <= 0 && this.enemyIDs.length === 0) { if (this.spawnQueue.length <= 0 && this.remainingEnemies.length === 0) {
this.gameState = GAME_STATES.BETWEEN_WAVES; this.gameState = GAME_STATES.BETWEEN_WAVES;
Script.setTimeout(this.startNextWave.bind(this), 5000); Script.setTimeout(this.startNextWave.bind(this), 5000);
@ -349,15 +385,17 @@ ShortbowGameManager.prototype = {
setLivesLeft: function(lives) { setLivesLeft: function(lives) {
lives = Math.max(0, lives); lives = Math.max(0, lives);
this.livesLeft = lives; this.livesLeft = lives;
Entities.editEntity(this.livesDisplayID, { //Entities.editEntity(this.livesDisplayID, {
text: this.livesLeft //text: this.livesLeft
}); //});
this.setDisplayText('lives', this.livesLeft);
}, },
setScore: function(score) { setScore: function(score) {
this.score = score; this.score = score;
Entities.editEntity(this.scoreDisplayID, { //Entities.editEntity(this.scoreDisplayID, {
text: utils.formatNumberWithCommas(this.score) //text: utils.formatNumberWithCommas(this.score)
}); //});
this.setDisplayText('score', this.score);
}, },
checkSpawnQueue: function() { checkSpawnQueue: function() {
var waveElapsedTime = Date.now() - this.spawnStartTime; var waveElapsedTime = Date.now() - this.spawnStartTime;
@ -372,7 +410,12 @@ ShortbowGameManager.prototype = {
}); });
var entityID = Entities.addEntity(baseEnemyProperties); var entityID = Entities.addEntity(baseEnemyProperties);
this.enemyIDs.push(entityID); //this.enemyIDs.push(entityID);
this.remainingEnemies.push({
id: entityID,
lastKnownPosition: baseEnemyProperties.position,
lastHeartbeat: Date.now()
});
this.spawnQueue.splice(0, 1); this.spawnQueue.splice(0, 1);
Script.setTimeout(function() { Script.setTimeout(function() {
var velocity = Entities.getEntityProperties(entityID, 'velocity').velocity; var velocity = Entities.getEntityProperties(entityID, 'velocity').velocity;
@ -390,17 +433,33 @@ ShortbowGameManager.prototype = {
//return; //return;
var enemiesEscaped = false; var enemiesEscaped = false;
for (var i = this.enemyIDs.length - 1; i >= 0; --i) { for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
var position = Entities.getEntityProperties(this.enemyIDs[i], 'position').position; var enemy = this.remainingEnemies[i];
if (position === undefined) { var timeSinceLastHeartbeat = Date.now() - enemy.lastHeartbeat;
// If the enemy can no longer be found, assume it was hit var distance = Vec3.distance(enemy.lastKnownPosition, this.rootPosition);
this.enemyIDs.splice(i, 1); if (timeSinceLastHeartbeat > 5000 || distance > 500) {
print("EXPIRING: ", enemy.id);
Entities.deleteEntity(enemy.id);
//this.enemyIDs.splice(i, 1);
this.remainingEnemies.splice(i, 1);
Audio.playSound(TARGET_HIT_SOUND, { Audio.playSound(TARGET_HIT_SOUND, {
volume: 1.0, volume: 1.0,
position: this.rootPosition, position: this.rootPosition,
}); });
this.setScore(this.score + 100); this.setScore(this.score + 100);
enemiesEscaped = true; enemiesEscaped = true;
}
//var position = Entities.getEntityProperties(this.enemyIDs[i], 'position').position;
//if (position === undefined || Vec3.distance(position, this.rootPosition) > 100) {
// If the enemy can no longer be found, assume it was hit
//this.enemyIDs.splice(i, 1);
//Audio.playSound(TARGET_HIT_SOUND, {
//volume: 1.0,
//position: this.rootPosition,
//});
//this.setScore(this.score + 100);
//enemiesEscaped = true;
//} else if (position.z < this.gatePosition.z) { //} else if (position.z < this.gatePosition.z) {
//Entities.deleteEntity(this.enemyIDs[i]); //Entities.deleteEntity(this.enemyIDs[i]);
//this.enemyIDs.splice(i, 1); //this.enemyIDs.splice(i, 1);
@ -410,7 +469,6 @@ ShortbowGameManager.prototype = {
//position: this.rootPosition //position: this.rootPosition
//}); //});
//enemiesEscaped = true; //enemiesEscaped = true;
}
} }
//print("LIVES LEFT: ", this.livesLeft, this.numberOfEntitiesLeftForWave); //print("LIVES LEFT: ", this.livesLeft, this.numberOfEntitiesLeftForWave);
if (this.livesLeft <= 0) { if (this.livesLeft <= 0) {
@ -437,7 +495,7 @@ ShortbowGameManager.prototype = {
print("GAME OVER"); print("GAME OVER");
// Update high score // Update high score
sendAndUpdateHighScore(this.highScoreDisplayID, this.rootEntityID, this.score, this.waveNumber, 1); sendAndUpdateHighScore(this.highscoreChannelName, this.rootEntityID, this.score, this.waveNumber, 1);
// Cleanup // Cleanup
Script.clearTimeout(this.nextWaveTimer); Script.clearTimeout(this.nextWaveTimer);
@ -464,42 +522,63 @@ ShortbowGameManager.prototype = {
} }
Script.setTimeout(function() { Script.setTimeout(function() {
Entities.editEntity(this.startButtonID, { visible: true }); //Entities.editEntity(this.startButtonID, { visible: true });
Messages.sendMessage(this.startButtonChannelName, 'show');
this.gameState = GAME_STATES.IDLE; this.gameState = GAME_STATES.IDLE;
}.bind(this), 3000); }.bind(this), 3000);
for (var i = 0; i < this.enemyIDs.length; i++) { for (var i = 0; i < this.remainingEnemies.length; i++) {
Entities.deleteEntity(this.enemyIDs[i]); Entities.deleteEntity(this.remainingEnemies[i].id);
} }
this.enemyIDs = []; this.remainingEnemies = [];
}, },
onEnemyKilled: function(entityID, position) { onEnemyKilled: function(entityID, position) {
if (this.gameState !== GAME_STATES.PLAYING) { if (this.gameState !== GAME_STATES.PLAYING) {
return; return;
} }
var idx = this.enemyIDs.indexOf(entityID); for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
if (idx >= 0) { var enemy = this.remainingEnemies[i];
this.enemyIDs.splice(idx, 1); if (enemy.id === entityID) {
Audio.playSound(TARGET_HIT_SOUND, { this.remainingEnemies.splice(i, 1);
volume: 1.0, Audio.playSound(TARGET_HIT_SOUND, {
//position: position, volume: 1.0,
position: this.rootPosition, //position: position,
}); position: this.rootPosition,
});
// Update score // Update score
this.setScore(this.score + 100); this.setScore(this.score + 100);
print("SCORE: ", this.score); print("SCORE: ", this.score);
this.checkWaveComplete(); this.checkWaveComplete();
break;
}
} }
//var idx = this.enemyIDs.indexOf(entityID);
//if (idx >= 0) {
// this.enemyIDs.splice(idx, 1);
// Audio.playSound(TARGET_HIT_SOUND, {
// volume: 1.0,
// //position: position,
// position: this.rootPosition,
// });
// // Update score
// this.setScore(this.score + 100);
// print("SCORE: ", this.score);
// this.checkWaveComplete();
//}
}, },
onEnemyEscaped: function(entityID, position) { onEnemyEscaped: function(entityID, position) {
var enemiesEscaped = false; var enemiesEscaped = false;
for (var i = this.enemyIDs.length - 1; i >= 0; --i) { for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
if (this.enemyIDs[i] == entityID) { var enemy = this.remainingEnemies[i];
Entities.deleteEntity(this.enemyIDs[i]); if (enemy.id == entityID) {
this.enemyIDs.splice(i, 1); Entities.deleteEntity(enemy.id);
this.remainingEnemies.splice(i, 1);
this.setLivesLeft(this.livesLeft - 1); this.setLivesLeft(this.livesLeft - 1);
Audio.playSound(ESCAPE_SOUND, { Audio.playSound(ESCAPE_SOUND, {
volume: 1.0, volume: 1.0,

View file

@ -0,0 +1,31 @@
(function() {
Script.include('utils.js');
Button = function() {
};
Button.prototype = {
preload: function(entityID) {
print("Loaded enemy entity");
this.entityID = entityID;
var props = Entities.getEntityProperties(this.entityID, 'parentID');
this.gameChannel = 'button-' + props.parentID;
Messages.subscribe(this.gameChannel);
Messages.messageReceived.connect(this, this.onReceivedMessage);
},
unload: function() {
Messages.unsubscribe(this.gameChannel);
Messages.messageReceived.disconnect(this, this.onReceivedMessage);
},
onReceivedMessage: function(channel, message, senderID) {
if (channel === this.gameChannel) {
Entities.editEntity(this.entityID, {
visible: message === 'show'
});
}
},
};
return new Button();
});