Skip to content

Instantly share code, notes, and snippets.

@OmarShehata
Created July 31, 2018 18:14
Show Gist options
  • Save OmarShehata/eeaa2fae407739f27a5cc27cdb6679ab to your computer and use it in GitHub Desktop.
Save OmarShehata/eeaa2fae407739f27a5cc27cdb6679ab to your computer and use it in GitHub Desktop.
CesiumJS Experimental Camera Tacking Class
/*global define*/
define([
'./Mine',
'./Boundary',
'./CameraTracking',
'./Coin',
'./postJson',
'./Ring',
'./Sound',
'./Drone',
'./ReplayData',
'./UI/GameOverUI',
'./UI/HighScoresUI',
'./UI/HUD',
'./UI/InstructionsUI',
'./UI/NewHighScoreUI',
'./UI/StartUI',
'./UI/ChooseUI',
'./UI/CountdownUI',
'Cesium/Core/Cartesian2',
'Cesium/Core/Cartesian3',
'Cesium/Core/Cartesian4',
'Cesium/Core/Cartographic',
'Cesium/Core/CatmullRomSpline',
'Cesium/Core/LinearSpline',
'Cesium/Core/CesiumTerrainProvider',
'Cesium/Core/Clock',
'Cesium/Core/Color',
'Cesium/Core/ColorGeometryInstanceAttribute',
'Cesium/Core/defaultValue',
'Cesium/Core/defined',
'Cesium/Core/defineProperties',
'Cesium/Core/Fullscreen',
'Cesium/Core/GeometryInstance',
'Cesium/Core/HeadingPitchRange',
'Cesium/Core/JulianDate',
'Cesium/Core/loadWithXhr',
'Cesium/Core/Math',
'Cesium/Core/Matrix3',
'Cesium/Core/Matrix4',
'Cesium/Core/Transforms',
'Cesium/Core/sampleTerrain',
'Cesium/Core/ScreenSpaceEventHandler',
'Cesium/Core/ScreenSpaceEventType',
'Cesium/Core/SphereGeometry',
'Cesium/Scene/BingMapsImageryProvider',
'Cesium/Scene/createTileMapServiceImageryProvider',
'Cesium/Scene/LabelCollection',
'Cesium/Scene/PolylineCollection',
'Cesium/Scene/Material',
'Cesium/Scene/Model',
'Cesium/Scene/PerInstanceColorAppearance',
'Cesium/Scene/Primitive',
'Cesium/Scene/Cesium3DTileset',
'Cesium/DataSources/PolylineGlowMaterialProperty',
'Cesium/DataSources/PolylineOutlineMaterialProperty',
'Cesium/Core/CorridorGeometry',
'Cesium/ThirdParty/when',
'Cesium/Widgets/Viewer/Viewer',
'domReady!'
], function(
Mine,
Boundary,
CameraTracking,
Coin,
postJson,
Ring,
Sound,
Drone,
ReplayData,
GameOverUI,
HighScoresUI,
HUD,
InstructionsUI,
NewHighScoreUI,
StartUI,
ChooseUI,
CountdownUI,
Cartesian2,
Cartesian3,
Cartesian4,
Cartographic,
CatmullRomSpline,
LinearSpline,
CesiumTerrainProvider,
Clock,
Color,
ColorGeometryInstanceAttribute,
defaultValue,
defined,
defineProperties,
Fullscreen,
GeometryInstance,
HeadingPitchRange,
JulianDate,
loadWithXhr,
CesiumMath,
Matrix3,
Matrix4,
Transforms,
sampleTerrain,
ScreenSpaceEventHandler,
ScreenSpaceEventType,
SphereGeometry,
BingMapsImageryProvider,
createTileMapServiceImageryProvider,
LabelCollection,
PolylineCollection,
Material,
Model,
PerInstanceColorAppearance,
Primitive,
Cesium3DTileset,
PolylineGlowMaterialProperty,
PolylineOutlineMaterialProperty,
CorridorGeometry,
when,
Viewer ) {
'use strict';
// debug options
var cameraTrackingEnabled = true;
// UI screens
var displayGameOver = false;
var displayNewBestTime = false;
var displayBestTimes = false;
var displayStartScreen = false;
var displayChooseScreen = false;
var displayInstructions = false;
var haveNewBestScore = false;
// Menu state
var inGameOver = false;
var inStartGame = false;
var inGame = false;
var inStartScreen = false;
var inDemoMode = false;
var currentTime = new JulianDate(2457511.333333);
var terrainProvider = new CesiumTerrainProvider({
url: '//assets.agi.com/stk-terrain/world'
});
var naturalEarthImageryProvider = createTileMapServiceImageryProvider({
url : 'NaturalEarthII/',
maximumLevel : 5,
credit : 'Imagery courtesy Natural Earth'
});
var bingImageryProvider = new BingMapsImageryProvider({
url : 'https://dev.virtualearth.net'
});
var viewer = new Viewer('cesiumContainer', {
animation: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
timeline: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
fullscreenButton: true,
baseLayerPicker: false,
scene3DOnly: true,
clock: new Clock({currentTime: currentTime}),
terrainProvider : terrainProvider,
imageryProvider : bingImageryProvider
});
var scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true;
scene.globe.enableLighting = true;
scene.screenSpaceCameraController.enableInputs = !cameraTrackingEnabled;
scene.screenSpaceCameraController.minimumZoomDistance = 0.0;
scene.shadowMap.enabled = false;
var sound = new Sound();
var hud = new HUD(scene);
var gameOverUI = new GameOverUI(scene);
var newHighScoreUI = new NewHighScoreUI(scene);
var highScores = new HighScoresUI(scene);
var startScreen = new StartUI(scene);
var chooseScreen = new ChooseUI(scene);
var instructions = new InstructionsUI(scene);
var countdownScreen = new CountdownUI(scene);
var replayData = new ReplayData();
var labels = scene.primitives.add(new LabelCollection());
var lines = scene.primitives.add(new PolylineCollection());
var timeLimit = 120 * 1000; // convert seconds to ms
var idleLimit = 10 * 1000; // number of seconds until demo mode
var lastKeyPressTime = Date.now();
var gameState = {
scene : scene,
sound : sound,
dt : 0.0,
resized : false,
startTime : 0,
time : timeLimit,
targetsHit : 0,
coinsHit : 0,
minesHit : 0,
targetMultiplier : 100,
coinMultiplier : 10,
timeMultiplier : .2,
finalTime : 0,
drone : undefined,
routeId : 0,
routes : [],
usingTileset : true,
offlineTileset: true,
mines : [],
coins : [],
rings : [],
track : [], // polygon path to show race track
labels : labels,
lines: lines
};
defineProperties(gameState, {
score : {
get : function () {
var s = 0;
s += gameState.targetsHit * gameState.targetMultiplier;
s += gameState.coinsHit * gameState.coinMultiplier;
s *= gameState.timeBonus;
return Math.floor(s);
}
},
timeBonus : {
get : function () {
var timeBonus = 1.0 + gameState.timeMultiplier * (Math.min(50, gameState.finalTime)/50);
return timeBonus;
}
}
});
var accel = 320.0;
var decel = 950.0;
var boostAcceleration = 500.0;
var gravity = 75.0;
// NYC!
//var longitude = -73.9662495;
//var latitude = 40.7834345;
// PHILLY!!
var longitude = -76.1641667;
var latitude = 39.9522222;
var height = 900000.0; // from space
var initialLocation = Cartesian3.fromDegrees(longitude, latitude, height);
var drone = new Drone({
gameState : gameState,
location : initialLocation,
acceleration : accel,
deceleration : decel,
boostAcceleration : boostAcceleration,
gravity : gravity
});
var cameraTracking = new CameraTracking({
gameState : gameState
});
var honkButtonDown = false;
/// key mappings
var stickerL_left = 37;
var stickerL_right = 39;
var stickerL_up = 38;
var stickerL_down = 40;
var stickerR_left = 68;
var stickerR_right = 71;
var stickerR_up = 82;
var stickerR_down = 70;
// var button_top = 18;
// var button_bot = 90;
// var button_left = 16
// var button_right = 88;
var buttonL_1_1 = 17;
var buttonL_1_2 = 18;
var buttonL_1_3 = 32;
var buttonL_2_1 = 16;
var buttonL_2_2 = 90;
var keyMappingSet = 2;
var KEY_MOVING_FORWARD = 0;
var KEY_MOVING_BACKWARD = 1;
var KEY_MOVING_LEFT = 2;
var KEY_MOVING_RIGHT = 3;
var KEY_ELEVATE = 4;
var KEY_DESCEND = 5;
var KEY_TURNING_LEFT = 6;
var KEY_TURNING_RIGHT = 7;
var keyMappings = [
[stickerR_up, stickerR_down, stickerR_left, stickerR_right, stickerL_up, stickerL_down, stickerL_left, stickerL_right],
[stickerR_up, stickerR_down, stickerR_left, stickerR_right, stickerL_down, stickerL_up, stickerL_left, stickerL_right],
[stickerL_up, stickerL_down, stickerL_left, stickerL_right, stickerR_up, stickerR_down, stickerR_left, stickerR_right],
[stickerL_up, stickerL_down, stickerL_left, stickerL_right, stickerR_down, stickerR_up, stickerR_left, stickerR_right]
];
init();
var tileset = undefined;
function initTileset(options) {
if (options.tileset.enabled) {
var url = options.tileset.onlineUrl;
if (options.tileset.offline) {
url = options.tileset.offlineUrl;
}
tileset = viewer.scene.primitives.add(new Cesium3DTileset({
url : url,
maximumScreenSpaceError : 4,
immediatelyLoadDesiredLOD: true,
loadSiblings: true,
maximumNumberOfLoadedTiles: options.tileset.maximumNumberOfLoadedTiles
}));
tileset.readyPromise.then(function(tileset) {
console.log('tileset ready')
var heightOffset = options.tileset.heightOffset;
var boundingSphere = tileset.boundingSphere;
// Position tileset
var cartographic = Cartographic.fromCartesian(boundingSphere.center);
var surface = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
var offset = Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
var translation = Cartesian3.subtract(offset, surface, new Cartesian3());
tileset.modelMatrix = Matrix4.fromTranslation(translation);
var longitude = options.initialPosition.lonDegrees;
var latitude = options.initialPosition.latDegrees;
var height = options.initialPosition.alt;
var initialLocation = Cartesian3.fromDegrees(longitude, latitude, height);
var dest = [initialLocation];
var heading = 0.0;
var headingPitchRange = new HeadingPitchRange();
headingPitchRange.pitch = CesiumMath.toRadians(-20.0);
headingPitchRange.range = 200.0;
headingPitchRange.heading = CesiumMath.toRadians(heading);
});
}
}
var deferred = when.defer();
var preloadedPromise = deferred.promise;
function init() {
when(loadWithXhr({
url : '/config',
method : 'GET'
}), function (optionsStr) {
var options = JSON.parse(optionsStr);
drone.model.readyPromise.then(function() {
lastMillis = Date.now();
scene.postRender.addEventListener(tick);
loadRoutes();
openStartMenu(true);
// if not using a tileset, register the tick function here
if (!options.tileset.enabled) {
scene.postRender.addEventListener(tick);
}
});
if (options.tileset.enabled) {
initTileset(options);
// starts the tick when the tileset is ready
when.all([tileset.readyPromise, drone.model.readyPromise, preloadedPromise]).then(function() {
console.log('setting up tick')
scene.postRender.addEventListener(tick);
});
}
hud.hudScale = options.hud.scale;
hud.hudBorder = options.hud.margin;
});
}
function setKey(event, isDown) {
// Admin
lastKeyPressTime = Date.now();
if (event.keyCode === 80) { // P
// Enter full screen
var fullScreen = viewer.fullscreenButton.viewModel;
Fullscreen.requestFullscreen(fullScreen.fullscreenElement);
return;
} else if (event.keyCode === 27) {
// Force refresh
window.location.reload(true);
return;
} else if (event.keyCode === 220) { // \
restart();
}
var isArrowKey =
event.keyCode === 82 || event.keyCode === 38 || // R Up
event.keyCode === 70 || event.keyCode === 40 || // F Down
event.keyCode === 68 || event.keyCode === 37 || // D Left
event.keyCode === 71 || event.keyCode === 39; // G Right
if (inDemoMode) {
openStartMenu(true);
} else if (displayStartScreen) {
if (isDown) {
if (event.keyCode === 82 || event.keyCode === 38) { // R Up
startScreen.selectMode = CesiumMath.mod(--startScreen.selectMode, StartUI.SELECT_MODE_COUNT);
} else if (event.keyCode === 70 || event.keyCode === 40) { // F Down
startScreen.selectMode = CesiumMath.mod(++startScreen.selectMode, StartUI.SELECT_MODE_COUNT);
} else if (!isArrowKey) {
if (startScreen.selectMode === StartUI.START_SELECTED) {
restart();
} else if (startScreen.selectMode === StartUI.CHOOSE_TRACK_SELECTED) {
openChooseTrack();
} else if (startScreen.selectMode === StartUI.BEST_TIMES_SELECTED) {
openBestTimes();
} else if (startScreen.selectMode === StartUI.INSTRUCTIONS_SELECTED) {
openInstructions();
}
}
}
} else if (displayChooseScreen) {
if (isDown) {
if (event.keyCode === 82 || event.keyCode === 38) { // R Up
chooseScreen.selectMode = CesiumMath.mod(--chooseScreen.selectMode, chooseScreen.numRoutes);
} else if (event.keyCode === 70 || event.keyCode === 40) { // F Down
chooseScreen.selectMode = CesiumMath.mod(++startScreen.selectMode, chooseScreen.numRoutes);
} else if (!isArrowKey) {
gameState.route = chooseScreen.selectMode;
openStartMenu(true);
}
}
} else if (displayBestTimes) {
if (isDown && !isArrowKey) {
if (inGameOver) {
openStartMenu(true);
} else {
openStartMenu(false);
}
}
} else if (displayGameOver) {
if (isDown && !isArrowKey) {
if (haveNewBestScore) {
haveNewBestScore = false;
openNewBestTimes();
} else {
openStartMenu(true);
}
}
} else if (displayInstructions) {
if (isDown && !isArrowKey) {
openStartMenu(false);
}
} else if (displayNewBestTime) {
if (isDown) {
if (event.keyCode === 82 || event.keyCode === 38) { // R Up
--newHighScoreUI.letters[newHighScoreUI.selected];
} else if (event.keyCode === 70 || event.keyCode === 40) { // F Down
++newHighScoreUI.letters[newHighScoreUI.selected];
} else if (event.keyCode === 68 || event.keyCode === 37) { // D Left
newHighScoreUI.selected = CesiumMath.mod(--newHighScoreUI.selected, newHighScoreUI.letters.length);
} else if (event.keyCode === 71 || event.keyCode === 39) { // G Right
newHighScoreUI.selected = CesiumMath.mod(++newHighScoreUI.selected, newHighScoreUI.letters.length);
} else if (!isArrowKey) {
var initials = newHighScoreUI.getString();
console.log(initials + " " + gameState.score);
postJson('/scores', {initials: initials, score: gameState.score});
openBestTimes();
}
}
} else if (inGame) {
if (event.keyCode === keyMappings[keyMappingSet][KEY_TURNING_LEFT] ) { // shift -> turn left
drone.turningLeft = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_TURNING_RIGHT] ) { //x -> turn right
drone.turningRight = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_ELEVATE]) { //alt -> elevate
drone.elevate = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_DESCEND] ) { // z -> down
drone.descend = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_MOVING_FORWARD] ) { // up arrow -> forward
drone.moveForward = isDown;
drone.pitchForwards = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_MOVING_BACKWARD]) { // down arrow -> backward
drone.moveBackward = isDown;
drone.pitchBackwards = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_MOVING_LEFT]) { // left arrow -> move left
drone.moveLeft = isDown;
drone.pitchLeft = isDown;
} else if (event.keyCode === keyMappings[keyMappingSet][KEY_MOVING_RIGHT]) { // right arrow -> move right
drone.moveRight = isDown;
drone.pitchRight = isDown;
} else if (event.keyCode === buttonL_1_1 ) {
keyMappingSet = 0;
drone.resetControls();
} else if (event.keyCode === buttonL_1_2 ) {
keyMappingSet = 1;
drone.resetControls();
} else if (event.keyCode === buttonL_2_1 ) {
keyMappingSet = 2;
drone.resetControls();
} else if (event.keyCode === buttonL_2_2 ) {
keyMappingSet = 3;
drone.resetControls();
}
}
}
document.addEventListener('keydown', function (event) {
setKey(event, true);
});
document.addEventListener('keyup', function (event) {
setKey(event, false);
});
function proceduralWaypoints() { // todo
var longitude = -75.1641667;
var latitude = 39.9522222;
var height = 181.31880833156316;
var initialLocation = Cartesian3.fromDegrees(longitude, latitude, height);
var numItems = 6;
var heightVariation = 250.0;
var radius = 1000.0;
var delta = 2 * Math.PI / numItems;
var transform = Transforms.eastNorthUpToFixedFrame(initialLocation);
var waypoints = [];
for (var i = 0.0; i < numItems; i++)
{
var theta = i * delta + delta * 0.5;
var x = radius * Math.cos(theta);
var y = radius * Math.sin(theta);
var z = Math.random() * heightVariation;
var ringLocation = new Cartesian4(x,y,z,1);
Matrix4.multiplyByVector(transform, ringLocation, ringLocation);
waypoints.push(ringLocation);
}
return waypoints;
}
function waypointTrack(waypoints, type) {
var times = [];
var positions = [];
for (var i = 0.0; i < waypoints.length; i++)
{
var wp = waypoints[i];
var ringLocation = Cartesian3.fromRadians(wp.lat, wp.lon, wp.height);
positions.push(ringLocation);
times.push(i);
}
var path = null;
if (type !== undefined && type === 'linear')
{
var path = new LinearSpline({times: times, points: positions});
}
else
{
var path = new CatmullRomSpline({times: times, points: positions});
}
return path;
}
function computeTrack(waypoints, type) {
/**
* Compute track based on the given waypoints.
* If the waypoint list is empty, creates a procedural track
* waypoints: list of lat,lon,alt objects
* type: optional string of either "linear" or "cubic"
* returns spline to interpolate the waypoints (each waypoint spaced evenly in time)
*/
if (waypoints.length === 0) {
waypoints = proceduralWaypoints(); // todo: could make a random track
}
return waypointTrack(waypoints, type);
}
function computeRouteObjects(path) {
/**
* Compute game objects around the given path
* path: Spline
* returns level object containing rings, mines, curve, coins
*/
var level = {
rings : [],
mines: [],
curve: [],
coins: []
};
// create rings
for (var t = 1.0; t < path.times.length; t+=1.0)
{
var ringLocation = path.evaluate(t);
// assume cubic polynomial
var lastLocation = path.evaluate(t-0.1);
var forwardX = new Cartesian3(1,0,0);
var leftY = new Cartesian3(0,1,0);
var upZ = new Cartesian3(0,0,1);
Cartesian3.subtract(ringLocation, lastLocation, forwardX);
forwardX.z = 0; // remove any pitch in the target
Cartesian3.normalize(forwardX, forwardX);
Cartesian3.cross(upZ, forwardX, leftY);
Cartesian3.cross(forwardX, leftY, upZ);
var ringOrientation = new Matrix3(forwardX.x, leftY.x, upZ.x,
forwardX.y, leftY.y, upZ.y,
forwardX.z, leftY.z, upZ.z);
var r = Math.max(0.2, Math.random());
var g = Math.max(0.2, Math.random());
var b = Math.max(0.2, Math.random());
var ringColor = new Color(r, g, b, 1.0);
level.rings.push({
location : ringLocation,
rotation : ringOrientation,
color : ringColor
});
}
var curve = [];
var sampleRate = 0.1;
var offsetRange = 15;
var mineChance = 0.4;
for (var t = 0.0; t < path.times[path.times.length-1]; t+=sampleRate)
{
var pos = path.evaluate(t);
level.curve.push(pos);
if (t > 0.1 && t < path.times[path.times.length-1]-sampleRate)
{
level.coins.push(pos);
}
var frac = t - Math.floor(t);
//console.log(frac);
var diceRoll = Math.random();
if (frac > 0.2 && frac < 0.8)
{
if (diceRoll < mineChance)
{
var minePos = new Cartesian3(pos.x, pos.y, pos.z);
var psi = Math.random() * Math.PI;
var theta = Math.random() * Math.PI * 2;
//minePos.x += offsetRange * Math.sin(theta) * Math.sin(psi);
minePos.y += offsetRange * Math.cos(theta);
minePos.z += offsetRange * Math.sin(theta);
level.mines.push(minePos);
}
}
}
return level;
}
function createRouteGeometry(level) {
/**
* Compute geometry for the objects defined in level
* level: object of lists of mines, coins, curve points, rings
* returns start pos and dir for the beginning of the path
*/
for (var i = 0; i < level.rings.length; i++) {
new Ring({
gameState : gameState,
location : level.rings[i].location,
rotation : level.rings[i].rotation,
color : level.rings[i].color
});
}
for (var i = 0; i < level.coins.length; i++) {
new Coin({
gameState : gameState,
location : level.coins[i]
});
}
for (var i = 0; i < level.mines.length; i++) {
new Mine({
gameState : gameState,
location : level.mines[i]
});
}
//console.log(curve);
var ellipsoid = gameState.scene.globe.ellipsoid;
var width = 2;
var vertices = [];
var normal = new Cartesian3(0,0,0);
var forward = new Cartesian3(0,0,0);
var backward = new Cartesian3(0,0,0);
var dir = new Cartesian3(0,0,0);
var right = new Cartesian3(0,0,0);
var p = new Cartesian3(0,0,0);
var lastLeft = null;
var lastRight = null;
for (var i = 1; i < level.curve.length-1; i++) {
Cartesian3.subtract(level.curve[i+1], level.curve[i-1], forward);
Cartesian3.normalize(forward, forward);
Cartesian3.subtract(level.curve[i], level.curve[i-1], backward);
Cartesian3.normalize(backward, backward);
Cartesian3.add(forward, backward, dir);
Cartesian3.normalize(dir, dir);
ellipsoid.geodeticSurfaceNormal(level.curve[i], normal);
Cartesian3.cross(dir, normal, right);
Cartesian3.normalize(right, right);
Cartesian3.multiplyByScalar(right, width, right);
var rightP = new Cartesian3(0,0,0);
var leftP = new Cartesian3(0,0,0);
Cartesian3.add(level.curve[i], right, rightP);
Cartesian3.subtract(level.curve[i], right, leftP);
if (lastRight !== null) {
var square = [new Cartesian3(lastRight.x, lastRight.y, lastRight.z), rightP, leftP,
new Cartesian3(lastLeft.x, lastLeft.y, lastLeft.z)];
var trackSegment = viewer.entities.add({
name : "track",
polygon : {
hierarchy : square,
perPositionHeight : true,
outline : true,
outlineColor : Color.DEEPSKYBLUE ,
material : Color.DODGERBLUE.withAlpha(0.1),
show : true }
});
gameState.track.push(trackSegment);
}
lastRight = rightP;
lastLeft = leftP;
}
var dir = new Cartesian3();
Cartesian3.subtract(level.curve[1], level.curve[0], dir);
Cartesian3.normalize(dir, dir);
var startDir = new Cartesian4(dir.x, dir.y, dir.z, 0);
var startPos = new Cartesian4(level.curve[0].x, level.curve[0].y, level.curve[0].z, 1);
return {pos: startPos, dir: startDir};
}
function clearRoute(gameState) {
console.log("CLEAR ROUTE");
gameState.lines.removeAll();
for (var i = 0; i < gameState.track.length; ++i) {
viewer.entities.remove(gameState.track[i]);
}
for (var i = 0; i < gameState.rings.length; ++i) {
gameState.rings[i].cleanup(gameState);
}
for (var i = 0; i < gameState.mines.length; ++i) {
gameState.mines[i].cleanup(gameState);
}
for (var i = 0; i < gameState.coins.length; ++i) {
gameState.coins[i].cleanup(gameState);
}
gameState.track = [];
gameState.rings = [];
gameState.coins = [];
gameState.mines = [];
}
function loadRoutes() {
when(loadWithXhr({
url : '/routes',
method : 'GET'
}), function (routes) {
gameState.routes = JSON.parse(routes);
});
}
function setupRoute() {
var waypoints = [];
var type = undefined;
if (gameState.routes.length !== 0 ||
gameState.routeId >= 0 ||
gameState.routeId < gameState.routes.length) {
waypoints = gameState.routes[gameState.routeId].waypoints;
type = gameState.routes[gameState.routeId].type;
}
var spline = computeTrack(waypoints, type); // return list of points
var level = computeRouteObjects(spline); // compute objects for the curve
var start = createRouteGeometry(level); // create and add geometry to the gameState
// ASN: disabled: not guaranteed to always look good
//replayData.startRecording(level);
return start;
}
function updateHighScores(endGame) {
when(loadWithXhr({
url : '/scores',
method : 'GET'
}), function (scores) {
scores = JSON.parse(scores);
if (endGame) {
var lowestHighScore = scores[scores.length - 1].score;
haveNewBestScore = gameState.score > lowestHighScore;
openGameOver();
}
highScores.scores = scores;
});
}
function openChooseTrack() {
displayStartScreen = false;
displayChooseScreen = true;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = false;
}
function openBestTimes() {
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = true;
displayInstructions = false;
updateHighScores();
}
function openInstructions() {
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = true;
}
function openGameOver() {
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = true;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = false;
}
function openNewBestTimes() {
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = true;
displayBestTimes = false;
displayInstructions = false;
}
function openStartMenu(reset) {
inGame = false;
inDemoMode = false;
inStartGame = false;
inStartScreen = true;
inGameOver = false;
displayStartScreen = true;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = false;
lastKeyPressTime = Date.now();
if (reset) {
heading = 50.0;
}
gameState.drone.resetControls();
}
function endGame() {
inGame = false;
inStartGame = false;
inStartScreen = false;
gameState.finalTime = Math.max(0, gameState.time);
lastKeyPressTime = Date.now();
if (!inDemoMode) {
inGameOver = true;
updateHighScores(true);
} else {
inDemoMode = false;
openStartMenu(true);
}
// ASN disabled
//replayData.stopRecording(gameState, true);
}
function restart() {
console.log("RESTART");
inGame = false;
inDemoMode = false;
inStartGame = false;
inStartScreen = false;
inGameOver = false;
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = false;
gameState.startTime = 0;
gameState.time = timeLimit;
gameState.targetsHit = 0;
gameState.minesHit = 0;
gameState.coinsHit = 0;
gameState.finalTime = 0;
gameState.routeId = chooseScreen.selectMode;
scene.camera.first = true;
// set track
clearRoute(gameState);
var start = setupRoute();
drone.reset(gameState, false, start.pos, start.dir);
// post new game started
postJson('/start');
startGame();
}
function startGame() {
if (!inStartGame) {
inStartGame = true;
countdownScreen.start(gameState);
}
}
function startDemo() {
if (!inDemoMode && !replayData.empty()) {
console.log("START DEMO");
inDemoMode = true;
inGame = false;
inStartGame = false;
inStartScreen = false;
inGameOver = false;
displayStartScreen = false;
displayChooseScreen = false;
displayGameOver = false;
displayNewBestTime = false;
displayBestTimes = false;
displayInstructions = false;
gameState.startTime = 0;
gameState.time = timeLimit;
gameState.targetsHit = 0;
gameState.minesHit = 0;
gameState.coinsHit = 0;
gameState.finalTime = 0;
gameState.routeId = chooseScreen.selectMode;
scene.camera.first = true;
// Initialize level
clearRoute(gameState);
var start = createRouteGeometry(replayData.randomReplay());
drone.reset(gameState, false, start.pos, start.dir);
replayData.startPlayback(gameState);
startGame();
}
}
function drawTrail(object, gameState, trailLength = 0.06) {
// if( (typeof object.position == 'undefined') ||
// (typeof object.prevPosition == 'undefined') ) {
// console.log("undefined");
// return ;
// }
//
// for (var i=0;i<object.position.length;i+=1.0){
// var curve = [];
// curve.push(object.prevPosition[i]);
// curve.push(object.position[i]);
// console.log( curve.length );
// var material = Material.fromType('PolylineGlow');
// //glowMaterial.color = Color.ORANGE;
//
// gameState.lines.add({
// positions : curve,
// width : 10.0,
// loop : false,
// // material : Material.fromType('PolylineGlow')
// material : material
// });
// var line = gameState.lines.get(gameState.lines.length-1);
// setInterval(function(line){
// line.show = false;
// gameState.lines.remove(line);
// }, gameState.dt * 10, line);
// }
var vel = object.velocity.clone();
var speed = Cartesian3.magnitude(vel);
if( speed > 1e-3){
Cartesian3.normalize(vel,vel);
Cartesian3.multiplyByScalar(vel, -1, vel);
var pos = new Cartesian3();
Cartesian3.multiplyByScalar(vel, speed * trailLength, vel);
Cartesian3.add(object.position, vel, pos);
var curve = [];
curve.push(pos);
curve.push(object.position);
var material = Material.fromType('Fade');
gameState.lines.add({
positions : curve,
width : 8.0,
loop : false,
// material : Material.fromType('PolylineGlow')
material : material
});
var line = gameState.lines.get(gameState.lines.length-1);
setInterval(function(line){
line.show = false;
gameState.lines.remove(line);
}, gameState.dt * 8, line);
}
console.log(gameState.lines.length);
}
// Camera animation in start screen
var heading = 0.0;
var headingPitchRange = new HeadingPitchRange();
var labelsToRemove = [];
var labelPositionScratch = new Cartesian3();
var labelColorScratch = new Color();
var lastMillis;
var lastDrawingBufferWidth = scene.context.drawingBufferWidth;
var lastDrawingBufferHeight = scene.context.drawingBufferHeight;
function tick() {
var now = Date.now();
if (inGame) {
gameState.time = timeLimit - (now - gameState.startTime);
}
var idle = (now - lastKeyPressTime);
if (!inGame && !inStartGame && !inDemoMode && idle > idleLimit) {
startDemo();
}
var drawingBufferWidth = scene.context.drawingBufferWidth;
var drawingBufferHeight = scene.context.drawingBufferHeight;
var resized = drawingBufferWidth !== lastDrawingBufferWidth || drawingBufferHeight !== lastDrawingBufferHeight;
lastDrawingBufferWidth = drawingBufferWidth;
lastDrawingBufferHeight = drawingBufferHeight;
gameState.resized = resized;
// dt is the delta-time since last tick, in seconds
var dt = (now - lastMillis) / 1000.0;
if (dt > 0.25) {
dt = 0.25;
}
lastMillis = now;
gameState.dt = dt;
if (inDemoMode && inGame) {
replayData.tick(gameState);
} else {
replayData.record(gameState);
}
if (!inGameOver) {
drone.tick(gameState, inDemoMode);
}
// trail effects here
// drawTrail(drone, gameState);
// ******* //
if (!inGameOver && cameraTrackingEnabled) {
cameraTracking.track(gameState, drone, inStartGame, inDemoMode);
}
var i;
for (i = 0; i < gameState.mines.length; ++i) {
gameState.mines[i].tick(gameState);
}
for (i = 0; i < gameState.coins.length; ++i) {
gameState.coins[i].tick(gameState);
}
for (i = 0; i < gameState.rings.length; ++i) {
gameState.rings[i].tick(gameState);
}
if (inGame && (drone.hitLastTarget || gameState.time < 0)) {
endGame();
}
// Camera animation in start screen
if (inStartScreen) {
heading = heading + dt * 2.0;
headingPitchRange.pitch = CesiumMath.toRadians(-20.0);
headingPitchRange.range = 4000.0;
headingPitchRange.heading = CesiumMath.toRadians(heading);
scene.camera.lookAt(initialLocation, headingPitchRange);
sound.playSound('wind');
}
else if (inGame) {
sound.playSound('race_loop');
}
else if (inGameOver) {
sound.playSound('game_over');
}
hud.show = inGame;
hud.tick(gameState);
if (inStartGame) {
countdownScreen.tick(gameState);
inStartGame = countdownScreen.show;
inGame = !countdownScreen.show;
if (inGame) {
gameState.startTime = now;
}
} else {
countdownScreen.show = false;
countdownScreen.tick(gameState);
}
gameOverUI.show = displayGameOver;
gameOverUI.tick(gameState);
newHighScoreUI.show = displayNewBestTime;
newHighScoreUI.tick(gameState);
highScores.show = displayBestTimes;
highScores.tick(gameState);
chooseScreen.show = displayChooseScreen;
chooseScreen.tick(gameState);
startScreen.show = displayStartScreen && !inDemoMode;
startScreen.tick(gameState);
instructions.show = displayInstructions;
instructions.tick(gameState);
var labels = gameState.labels;
var length = labels.length;
labelsToRemove.length = 0;
labels.modelMatrix = drone.modelMatrix;
for (i = 0; i < length; ++i) {
var label = labels.get(i);
if (Cartesian3.equals(Cartesian3.ZERO, label.position)) {
label.position = Cartesian3.fromElements(0.0, 0.0, drone.model.boundingSphere.radius * drone.model.scale, labelPositionScratch);
label.fillColor = Color.fromAlpha(label.fillColor, 1.0, labelColorScratch);
} else {
label.position = Cartesian3.fromElements(0.0, 0.0, label.position.z + 0.01 * drone.model.scale, labelPositionScratch);
label.fillColor = Color.fromAlpha(label.fillColor, label.fillColor.alpha - 0.05, labelColorScratch);
}
if (label.fillColor.alpha <= 0.0) {
labelsToRemove.push(label);
}
}
length = labelsToRemove.length;
for (i = 0; i < length; ++i) {
labels.remove(labelsToRemove[i]);
}
}
});
/*global define*/
define([
'Cesium/Core/Cartesian3',
'Cesium/Core/Cartesian4',
'Cesium/Core/Cartographic',
'Cesium/Core/defaultValue',
'Cesium/Core/defined',
'Cesium/Core/Ellipsoid',
'Cesium/Core/HeadingPitchRange',
'Cesium/Core/Math',
'Cesium/Core/Matrix3',
'Cesium/Core/Matrix4',
'Cesium/Core/Quaternion',
'Cesium/Core/Transforms',
'Cesium/Scene/SceneMode'
], function(
Cartesian3,
Cartesian4,
Cartographic,
defaultValue,
defined,
Ellipsoid,
HeadingPitchRange,
CesiumMath,
Matrix3,
Matrix4,
Quaternion,
Transforms,
SceneMode) {
'use strict';
function CameraTracking(options) {
this.camera = options.gameState.scene.camera;
this.cameraHeight = defaultValue(options.cameraHeight, 4.0);
this.trailingDistance = defaultValue(options.trailingDistance, 13.0);
this.pitch = defaultValue(options.pitch, -CesiumMath.toRadians(10.0));
this.minimumZoomDistance = defaultValue(options.minimumZoomDistance, 6.0);
this.moveSpeed = defaultValue(options.moveSpeed, 1.25);
this.maxOffset = defaultValue(options.maxOffset, Cartesian3.fromElements(0.0, this.trailingDistance, this.cameraHeight, offsetScratch));
this.first = true;
this.prevOffset = new Cartesian3();
this.xFactor = 0.000002;
this.yFactor = 0.0022;
this.yFactorFar = 0.0038;
this.zFactor = 0.0032;
// this.xFactor = 0.520;
// this.yFactor = 0.122;
// this.yFactorFar = 0.188;
// this.zFactor = 0.18;
//
this.headingFactor = 0.01;
this.pitchFactor = 0.03;
this.distanceStableFactor = 0.08;
this.distanceNearFactor = 0.06;
this.distanceFarFactor = 0.16;
this.stableDistance = 20;
this.nearDistance = 18;
this.farDistance = 28;
}
var transformScratch = new Matrix4();
var cartographicScratch = new Cartographic();
var positionScratch = new Cartesian4();
var directionScratch = new Cartesian4();
var upScratch = new Cartesian4();
var rightScratch = new Cartesian4();
var offsetScratch = new Cartesian3();
function adjustHeightForTerrain(tracking, scene) {
var mode = scene.mode;
var globe = scene.globe;
if (!defined(globe) || mode === SceneMode.SCENE2D || mode === SceneMode.MORPHING) {
return;
}
var camera = scene.camera;
var ellipsoid = globe.ellipsoid;
var projection = scene.mapProjection;
var transform;
var mag;
if (!Matrix4.equals(camera.transform, Matrix4.IDENTITY)) {
transform = Matrix4.clone(camera.transform, transformScratch);
mag = Cartesian3.magnitude(camera.position);
camera._setTransform(Matrix4.IDENTITY);
}
var cartographic = cartographicScratch;
if (mode === SceneMode.SCENE3D) {
ellipsoid.cartesianToCartographic(camera.position, cartographic);
}
var height = globe.getHeight(cartographic);
if (defined(height)) {
height += tracking.minimumZoomDistance;
if (cartographic.height < height) {
cartographic.height = height;
if (mode === SceneMode.SCENE3D) {
ellipsoid.cartographicToCartesian(cartographic, camera.position);
} else {
projection.project(cartographic, camera.position);
}
}
}
if (defined(transform)) {
camera._setTransform(transform);
Cartesian3.normalize(camera.position, camera.position);
Cartesian3.negate(camera.position, camera.direction);
Cartesian3.multiplyByScalar(camera.position, mag, camera.position);
Cartesian3.normalize(camera.direction, camera.direction);
Cartesian3.cross(camera.direction, camera.up, camera.right);
Cartesian3.cross(camera.right, camera.direction, camera.up);
Cartesian3.normalize(camera.right, camera.right);
Cartesian3.normalize(camera.up, camera.up);
}
}
function printVector(vec) {
console.log(vec.x + "," + vec.y + "," + vec.z );
// console.log(vec.x + "," + vec.y + "," + vec.z + " [mag = " + Cartesian3.magnitude(vec) + "]");
}
function lerp(a,b,t) {
var r = a * t + b * (1 - t);
return r;
}
CameraTracking.prototype.track = function(gameState, object, startScreen, demoMode) {
var scene = gameState.scene;
var camera = this.camera;
var ellipsoid = scene.mapProjection.ellipsoid;
var modelMatrix = object.modelMatrix;
var position = Matrix4.getColumn(modelMatrix, 3, positionScratch);
var direction = Matrix4.getColumn(modelMatrix, 1, directionScratch);
var up = ellipsoid.geodeticSurfaceNormal(position, upScratch);
Cartesian3.normalize(up, up);
var right = Cartesian3.cross(direction, up, rightScratch);
Cartesian3.normalize(right, right);
Cartesian3.cross(up, right, direction);
Cartesian3.normalize(direction, direction);
var transform = transformScratch;
// transform = object.modelMatrix.clone();
Matrix4.setColumn(transform, 0, right, transform);
Matrix4.setColumn(transform, 1, direction, transform);
Matrix4.setColumn(transform, 2, up, transform);
Matrix4.setColumn(transform, 3, position, transform);
// calculate camera position in transform frame of reference
var localOffset = new Cartesian3();
if (!demoMode || startScreen) {
var inverseModelMatrix = new Matrix4();
inverseModelMatrix = Matrix4.inverseTransformation(transform, inverseModelMatrix);
localOffset = Matrix4.multiplyByPoint(inverseModelMatrix, camera.positionWC, localOffset);
var xPow = Math.pow(this.xFactor, gameState.dt);
localOffset.x = localOffset.x * xPow;
var yPow;
if (object.moveForward && !object.moveBackward){
yPow = Math.pow(this.yFactorFar, gameState.dt);
} else {
yPow = Math.pow(this.yFactor, gameState.dt);
}
localOffset.y = (1-yPow) * this.trailingDistance + yPow * localOffset.y;
if(localOffset.y < 2) {
localOffset.y = 2;
}
var zPow = Math.pow(this.zFactor, gameState.dt);
localOffset.z = (1-zPow)*(this.cameraHeight) + zPow * localOffset.z;
var lerpFactor = 0.78;
if (!startScreen && gameState.dt > Number.EPSILON) {
lerpFactor = Math.pow(0.01,gameState.dt)
}
if(typeof this.prevOffset == 'undefined'){
this.prevOffset = localOffset.clone();
}
Cartesian3.lerp(localOffset, this.prevOffset, lerpFactor, localOffset);
this.prevOffset = localOffset.clone();
} else {
var xPow = Math.pow(this.xFactor, gameState.dt);
localOffset.x = 0; //localOffset.x * xPow;
var yPow = Math.pow(this.yFactor, gameState.dt);
localOffset.y = 15; //(1-yPow) * this.trailingDistance + yPow * localOffset.y;
var zPow = Math.pow(this.zFactor, gameState.dt);
localOffset.z = 5; //this.cameraHeight;
}
camera.lookAtTransform(transform, localOffset);
};
return CameraTracking;
});
@Apizz789
Copy link

Apizz789 commented Jul 4, 2022

Good Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment