mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2026-04-27 13:11:42 +00:00
do more work on replays
This commit is contained in:
parent
24c87da4fd
commit
841b059f6e
5 changed files with 211 additions and 17 deletions
|
|
@ -214,7 +214,9 @@ class CameraController extends Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.level.isWatching) {
|
if (!this.level.isWatching) {
|
||||||
this.level.replay.recordCameraState(CameraPitch, CameraYaw);
|
if (this.level.isRecording) {
|
||||||
|
this.level.replay.recordCameraState(CameraPitch, CameraYaw);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
CameraPitch = this.level.replay.currentPlaybackFrame.cameraPitch;
|
CameraPitch = this.level.replay.currentPlaybackFrame.cameraPitch;
|
||||||
CameraYaw = this.level.replay.currentPlaybackFrame.cameraYaw;
|
CameraYaw = this.level.replay.currentPlaybackFrame.cameraYaw;
|
||||||
|
|
|
||||||
|
|
@ -1335,7 +1335,11 @@ class Marble extends GameObject {
|
||||||
public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
|
public function update(timeState:TimeState, collisionWorld:CollisionWorld, pathedInteriors:Array<PathedInterior>) {
|
||||||
var move = new Move();
|
var move = new Move();
|
||||||
move.d = new Vector();
|
move.d = new Vector();
|
||||||
if (this.controllable && this.mode != Finish && !MarbleGame.instance.paused && !this.level.isWatching) {
|
if (this.controllable
|
||||||
|
&& this.mode != Finish
|
||||||
|
&& !MarbleGame.instance.paused
|
||||||
|
&& !this.level.isWatching
|
||||||
|
&& this.level.isRecording) {
|
||||||
if (Key.isDown(Settings.controlsSettings.forward)) {
|
if (Key.isDown(Settings.controlsSettings.forward)) {
|
||||||
move.d.x -= 1;
|
move.d.x -= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -1367,16 +1371,20 @@ class Marble extends GameObject {
|
||||||
move.powerup = true;
|
move.powerup = true;
|
||||||
move.d = new Vector(this.level.replay.currentPlaybackFrame.marbleX, this.level.replay.currentPlaybackFrame.marbleY, 0);
|
move.d = new Vector(this.level.replay.currentPlaybackFrame.marbleX, this.level.replay.currentPlaybackFrame.marbleY, 0);
|
||||||
} else {
|
} else {
|
||||||
this.level.replay.recordMarbleStateFlags(move.jump, move.powerup, false);
|
if (this.level.isRecording) {
|
||||||
this.level.replay.recordMarbleInput(move.d.x, move.d.y);
|
this.level.replay.recordMarbleStateFlags(move.jump, move.powerup, false);
|
||||||
|
this.level.replay.recordMarbleInput(move.d.x, move.d.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playedSounds = [];
|
playedSounds = [];
|
||||||
advancePhysics(timeState, move, collisionWorld, pathedInteriors);
|
advancePhysics(timeState, move, collisionWorld, pathedInteriors);
|
||||||
|
|
||||||
if (!this.level.isWatching)
|
if (!this.level.isWatching) {
|
||||||
this.level.replay.recordMarbleState(this.getAbsPos().getPosition(), this.velocity, this.getRotationQuat(), this.omega);
|
if (this.level.isRecording) {
|
||||||
else {
|
this.level.replay.recordMarbleState(this.getAbsPos().getPosition(), this.velocity, this.getRotationQuat(), this.omega);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
var expectedPos = this.level.replay.currentPlaybackFrame.marblePosition.clone();
|
var expectedPos = this.level.replay.currentPlaybackFrame.marblePosition.clone();
|
||||||
var expectedVel = this.level.replay.currentPlaybackFrame.marbleVelocity.clone();
|
var expectedVel = this.level.replay.currentPlaybackFrame.marbleVelocity.clone();
|
||||||
var expectedOmega = this.level.replay.currentPlaybackFrame.marbleAngularVelocity.clone();
|
var expectedOmega = this.level.replay.currentPlaybackFrame.marbleAngularVelocity.clone();
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ class MarbleWorld extends Scheduler {
|
||||||
// Replay
|
// Replay
|
||||||
public var replay:Replay;
|
public var replay:Replay;
|
||||||
public var isWatching:Bool = false;
|
public var isWatching:Bool = false;
|
||||||
|
public var isRecording:Bool = true;
|
||||||
|
|
||||||
// Loading
|
// Loading
|
||||||
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
var resourceLoadFuncs:Array<(() -> Void)->Void> = [];
|
||||||
|
|
@ -320,9 +321,10 @@ class MarbleWorld extends Scheduler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restart() {
|
public function restart() {
|
||||||
if (!this.isWatching)
|
if (!this.isWatching) {
|
||||||
this.replay.clear();
|
this.replay.clear();
|
||||||
else
|
this.isRecording = true;
|
||||||
|
} else
|
||||||
this.replay.rewind();
|
this.replay.rewind();
|
||||||
this.timeState.currentAttemptTime = 0;
|
this.timeState.currentAttemptTime = 0;
|
||||||
this.timeState.gameplayClock = 0;
|
this.timeState.gameplayClock = 0;
|
||||||
|
|
@ -339,6 +341,33 @@ class MarbleWorld extends Scheduler {
|
||||||
this.playGui.formatGemCounter(this.gemCount, this.totalGems);
|
this.playGui.formatGemCounter(this.gemCount, this.totalGems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record/Playback trapdoor and landmine states
|
||||||
|
var tidx = 0;
|
||||||
|
var lidx = 0;
|
||||||
|
for (dtss in this.dtsObjects) {
|
||||||
|
if (dtss is Trapdoor) {
|
||||||
|
var trapdoor:Trapdoor = cast dtss;
|
||||||
|
if (!this.isWatching) {
|
||||||
|
this.replay.recordTrapdoorState(trapdoor.lastContactTime - this.timeState.timeSinceLoad, trapdoor.lastDirection, trapdoor.lastCompletion);
|
||||||
|
} else {
|
||||||
|
var state = this.replay.getTrapdoorState(tidx);
|
||||||
|
trapdoor.lastContactTime = state.lastContactTime + this.timeState.timeSinceLoad;
|
||||||
|
trapdoor.lastDirection = state.lastDirection;
|
||||||
|
trapdoor.lastCompletion = state.lastCompletion;
|
||||||
|
}
|
||||||
|
tidx++;
|
||||||
|
}
|
||||||
|
if (dtss is LandMine) {
|
||||||
|
var landmine:LandMine = cast dtss;
|
||||||
|
if (!this.isWatching) {
|
||||||
|
this.replay.recordLandMineState(landmine.disappearTime - this.timeState.timeSinceLoad);
|
||||||
|
} else {
|
||||||
|
landmine.disappearTime = this.replay.getLandMineState(lidx) + this.timeState.timeSinceLoad;
|
||||||
|
}
|
||||||
|
lidx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var startquat = this.getStartPositionAndOrientation();
|
var startquat = this.getStartPositionAndOrientation();
|
||||||
|
|
||||||
this.marble.setPosition(startquat.position.x, startquat.position.y, startquat.position.z + 3);
|
this.marble.setPosition(startquat.position.x, startquat.position.y, startquat.position.z + 3);
|
||||||
|
|
@ -819,9 +848,11 @@ class MarbleWorld extends Scheduler {
|
||||||
if (!_ready) {
|
if (!_ready) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.isWatching)
|
if (!this.isWatching) {
|
||||||
this.replay.startFrame();
|
if (this.isRecording) {
|
||||||
else {
|
this.replay.startFrame();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (!this.replay.advance(dt)) {
|
if (!this.replay.advance(dt)) {
|
||||||
if (Util.isTouchDevice()) {
|
if (Util.isTouchDevice()) {
|
||||||
MarbleGame.instance.touchInput.hideControls(@:privateAccess this.playGui.playGuiCtrl);
|
MarbleGame.instance.touchInput.hideControls(@:privateAccess this.playGui.playGuiCtrl);
|
||||||
|
|
@ -834,6 +865,7 @@ class MarbleWorld extends Scheduler {
|
||||||
#if js
|
#if js
|
||||||
pointercontainer.hidden = false;
|
pointercontainer.hidden = false;
|
||||||
#end
|
#end
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -859,8 +891,11 @@ class MarbleWorld extends Scheduler {
|
||||||
ProfilerUI.measure("updateAudio");
|
ProfilerUI.measure("updateAudio");
|
||||||
AudioManager.update(this.scene);
|
AudioManager.update(this.scene);
|
||||||
|
|
||||||
if (!this.isWatching)
|
if (!this.isWatching) {
|
||||||
this.replay.endFrame();
|
if (this.isRecording) {
|
||||||
|
this.replay.endFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.outOfBounds && this.finishTime == null && Key.isDown(Settings.controlsSettings.powerup)) {
|
if (this.outOfBounds && this.finishTime == null && Key.isDown(Settings.controlsSettings.powerup)) {
|
||||||
this.clearSchedule();
|
this.clearSchedule();
|
||||||
|
|
@ -958,7 +993,7 @@ class MarbleWorld extends Scheduler {
|
||||||
this.timeState.gameplayClock = finishTime.gameplayClock;
|
this.timeState.gameplayClock = finishTime.gameplayClock;
|
||||||
playGui.formatTimer(this.timeState.gameplayClock);
|
playGui.formatTimer(this.timeState.gameplayClock);
|
||||||
|
|
||||||
if (!this.isWatching)
|
if (!this.isWatching && this.isRecording)
|
||||||
this.replay.recordTimeState(timeState.currentAttemptTime, timeState.gameplayClock, this.bonusTime);
|
this.replay.recordTimeState(timeState.currentAttemptTime, timeState.gameplayClock, this.bonusTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1160,6 +1195,7 @@ class MarbleWorld extends Scheduler {
|
||||||
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
|
||||||
pointercontainer.hidden = false;
|
pointercontainer.hidden = false;
|
||||||
#end
|
#end
|
||||||
|
this.isRecording = false; // Stop recording here
|
||||||
if (Util.isTouchDevice()) {
|
if (Util.isTouchDevice()) {
|
||||||
MarbleGame.instance.touchInput.setControlsEnabled(false);
|
MarbleGame.instance.touchInput.setControlsEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
150
src/Replay.hx
150
src/Replay.hx
|
|
@ -1,5 +1,9 @@
|
||||||
package src;
|
package src;
|
||||||
|
|
||||||
|
import haxe.io.Bytes;
|
||||||
|
import haxe.io.BytesBuffer;
|
||||||
|
import dif.io.BytesReader;
|
||||||
|
import dif.io.BytesWriter;
|
||||||
import haxe.EnumFlags;
|
import haxe.EnumFlags;
|
||||||
import h3d.Quat;
|
import h3d.Quat;
|
||||||
import h3d.Vector;
|
import h3d.Vector;
|
||||||
|
|
@ -94,13 +98,96 @@ class ReplayFrame {
|
||||||
|
|
||||||
return interpFrame;
|
return interpFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function write(bw:BytesWriter) {
|
||||||
|
bw.writeFloat(this.time);
|
||||||
|
bw.writeFloat(this.clockTime);
|
||||||
|
bw.writeFloat(this.bonusTime);
|
||||||
|
bw.writeFloat(this.marblePosition.x);
|
||||||
|
bw.writeFloat(this.marblePosition.y);
|
||||||
|
bw.writeFloat(this.marblePosition.z);
|
||||||
|
bw.writeFloat(this.marbleVelocity.x);
|
||||||
|
bw.writeFloat(this.marbleVelocity.y);
|
||||||
|
bw.writeFloat(this.marbleVelocity.z);
|
||||||
|
bw.writeFloat(this.marbleOrientation.x);
|
||||||
|
bw.writeFloat(this.marbleOrientation.y);
|
||||||
|
bw.writeFloat(this.marbleOrientation.z);
|
||||||
|
bw.writeFloat(this.marbleOrientation.w);
|
||||||
|
bw.writeFloat(this.marbleAngularVelocity.x);
|
||||||
|
bw.writeFloat(this.marbleAngularVelocity.y);
|
||||||
|
bw.writeFloat(this.marbleAngularVelocity.z);
|
||||||
|
bw.writeByte(this.marbleStateFlags.toInt());
|
||||||
|
bw.writeFloat(this.cameraPitch);
|
||||||
|
bw.writeFloat(this.cameraYaw);
|
||||||
|
bw.writeFloat(this.marbleX);
|
||||||
|
bw.writeFloat(this.marbleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(br:BytesReader) {
|
||||||
|
this.time = br.readFloat();
|
||||||
|
this.clockTime = br.readFloat();
|
||||||
|
this.bonusTime = br.readFloat();
|
||||||
|
this.marblePosition = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||||
|
this.marbleVelocity = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||||
|
this.marbleOrientation = new Quat(br.readFloat(), br.readFloat(), br.readFloat(), br.readFloat());
|
||||||
|
this.marbleAngularVelocity = new Vector(br.readFloat(), br.readFloat(), br.readFloat());
|
||||||
|
this.marbleStateFlags = EnumFlags.ofInt(br.readByte());
|
||||||
|
this.cameraPitch = br.readFloat();
|
||||||
|
this.cameraYaw = br.readFloat();
|
||||||
|
this.marbleX = br.readFloat();
|
||||||
|
this.marbleY = br.readFloat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:publicFields
|
||||||
|
class ReplayInitialState {
|
||||||
|
var trapdoorLastContactTimes:Array<Float> = [];
|
||||||
|
var trapdoorLastDirections:Array<Int> = [];
|
||||||
|
var trapdoorLastCompletions:Array<Float> = [];
|
||||||
|
var landMineDisappearTimes:Array<Float> = [];
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function write(bw:BytesWriter) {
|
||||||
|
bw.writeInt16(this.trapdoorLastContactTimes.length);
|
||||||
|
for (time in this.trapdoorLastContactTimes) {
|
||||||
|
bw.writeFloat(time);
|
||||||
|
}
|
||||||
|
for (dir in this.trapdoorLastDirections) {
|
||||||
|
bw.writeByte(dir);
|
||||||
|
}
|
||||||
|
for (completion in this.trapdoorLastCompletions) {
|
||||||
|
bw.writeFloat(completion);
|
||||||
|
}
|
||||||
|
bw.writeInt16(this.landMineDisappearTimes.length);
|
||||||
|
for (time in this.landMineDisappearTimes) {
|
||||||
|
bw.writeFloat(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(br:BytesReader) {
|
||||||
|
var trapdoorCount = br.readInt16();
|
||||||
|
for (i in 0...trapdoorCount) {
|
||||||
|
this.trapdoorLastContactTimes.push(br.readFloat());
|
||||||
|
}
|
||||||
|
for (i in 0...trapdoorCount) {
|
||||||
|
this.trapdoorLastDirections.push(br.readByte());
|
||||||
|
}
|
||||||
|
for (i in 0...trapdoorCount) {
|
||||||
|
this.trapdoorLastCompletions.push(br.readFloat());
|
||||||
|
}
|
||||||
|
var landMineCount = br.readInt16();
|
||||||
|
for (i in 0...landMineCount) {
|
||||||
|
this.landMineDisappearTimes.push(br.readFloat());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Replay {
|
class Replay {
|
||||||
public var mission:String;
|
public var mission:String;
|
||||||
|
|
||||||
var frames:Array<ReplayFrame>;
|
var frames:Array<ReplayFrame>;
|
||||||
|
var initialState:ReplayInitialState;
|
||||||
var currentRecordFrame:ReplayFrame;
|
var currentRecordFrame:ReplayFrame;
|
||||||
|
|
||||||
public var currentPlaybackFrame:ReplayFrame;
|
public var currentPlaybackFrame:ReplayFrame;
|
||||||
|
|
@ -110,6 +197,7 @@ class Replay {
|
||||||
|
|
||||||
public function new(mission:String) {
|
public function new(mission:String) {
|
||||||
this.mission = mission;
|
this.mission = mission;
|
||||||
|
this.initialState = new ReplayInitialState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function startFrame() {
|
public function startFrame() {
|
||||||
|
|
@ -153,6 +241,28 @@ class Replay {
|
||||||
currentRecordFrame.cameraYaw = yaw;
|
currentRecordFrame.cameraYaw = yaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recordTrapdoorState(lastContactTime:Float, lastDirection:Int, lastCompletion:Float) {
|
||||||
|
initialState.trapdoorLastContactTimes.push(lastContactTime);
|
||||||
|
initialState.trapdoorLastDirections.push(lastDirection);
|
||||||
|
initialState.trapdoorLastCompletions.push(lastCompletion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function recordLandMineState(disappearTime:Float) {
|
||||||
|
initialState.landMineDisappearTimes.push(disappearTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTrapdoorState(idx:Int) {
|
||||||
|
return {
|
||||||
|
lastContactTime: initialState.trapdoorLastContactTimes[idx],
|
||||||
|
lastDirection: initialState.trapdoorLastDirections[idx],
|
||||||
|
lastCompletion: initialState.trapdoorLastCompletions[idx]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLandMineState(idx:Int) {
|
||||||
|
return initialState.landMineDisappearTimes[idx];
|
||||||
|
}
|
||||||
|
|
||||||
public function clear() {
|
public function clear() {
|
||||||
this.frames = [];
|
this.frames = [];
|
||||||
currentRecordFrame = null;
|
currentRecordFrame = null;
|
||||||
|
|
@ -188,4 +298,42 @@ class Replay {
|
||||||
this.currentPlaybackFrame = null;
|
this.currentPlaybackFrame = null;
|
||||||
this.currentPlaybackFrameIdx = 0;
|
this.currentPlaybackFrameIdx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function write() {
|
||||||
|
var bw = new BytesWriter();
|
||||||
|
|
||||||
|
bw.writeStr(this.mission);
|
||||||
|
this.initialState.write(bw);
|
||||||
|
bw.writeInt32(this.frames.length);
|
||||||
|
for (frame in this.frames) {
|
||||||
|
frame.write(bw);
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf = bw.getBuffer();
|
||||||
|
var bufsize = buf.length;
|
||||||
|
var compressed = haxe.zip.Compress.run(bw.getBuffer(), 7);
|
||||||
|
|
||||||
|
var finalB = new BytesBuffer();
|
||||||
|
finalB.addInt32(bufsize);
|
||||||
|
finalB.addBytes(compressed, 4, compressed.length);
|
||||||
|
|
||||||
|
return finalB.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(data:Bytes) {
|
||||||
|
var uncompressedLength = data.getInt32(0);
|
||||||
|
var compressedData = data.sub(4, data.length - 4);
|
||||||
|
|
||||||
|
var uncompressed = haxe.zip.Uncompress.run(compressedData, uncompressedLength);
|
||||||
|
var br = new BytesReader(uncompressed);
|
||||||
|
this.mission = br.readStr();
|
||||||
|
this.initialState.read(br);
|
||||||
|
var frameCount = br.readInt32();
|
||||||
|
this.frames = [];
|
||||||
|
for (i in 0...frameCount) {
|
||||||
|
var frame = new ReplayFrame();
|
||||||
|
frame.read(br);
|
||||||
|
this.frames.push(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import src.MarbleWorld;
|
||||||
class Trapdoor extends DtsObject {
|
class Trapdoor extends DtsObject {
|
||||||
var lastContactTime = -1e8;
|
var lastContactTime = -1e8;
|
||||||
var timeout:Float = 0.2;
|
var timeout:Float = 0.2;
|
||||||
var lastDirection:Float;
|
var lastDirection:Int;
|
||||||
var lastCompletion:Float = 0;
|
var lastCompletion:Float = 0;
|
||||||
|
|
||||||
public function new() {
|
public function new() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue