Created
July 31, 2018 18:14
-
-
Save OmarShehata/eeaa2fae407739f27a5cc27cdb6679ab to your computer and use it in GitHub Desktop.
CesiumJS Experimental Camera Tacking Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*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]); | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*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; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Good Code