mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
spectator mode
This commit is contained in:
parent
57873e6301
commit
3a928a6014
7 changed files with 301 additions and 4 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package src;
|
||||
|
||||
import net.Net;
|
||||
import mis.MisParser;
|
||||
import h3d.col.Bounds;
|
||||
import h3d.col.Plane;
|
||||
|
|
@ -28,6 +29,7 @@ import h3d.Vector;
|
|||
import hxsl.Types.Matrix;
|
||||
import h3d.scene.Scene;
|
||||
import src.Gamepad;
|
||||
import src.MarbleGame;
|
||||
|
||||
enum CameraMode {
|
||||
FreeOrbit;
|
||||
|
|
@ -71,6 +73,9 @@ class CameraController extends Object {
|
|||
public var finish:Bool = false;
|
||||
public var overview:Bool = false;
|
||||
|
||||
var spectate:Bool = false;
|
||||
var spectateMarbleIndex:Int = -1;
|
||||
|
||||
var overviewCenter:Vector;
|
||||
var overviewWidth:Vector;
|
||||
var overviewHeight:Float;
|
||||
|
|
@ -119,6 +124,14 @@ class CameraController extends Object {
|
|||
#end
|
||||
}
|
||||
|
||||
public function enableSpectate() {
|
||||
spectate = true;
|
||||
}
|
||||
|
||||
public function stopSpectate() {
|
||||
spectate = false;
|
||||
}
|
||||
|
||||
public function orbit(mouseX:Float, mouseY:Float, isTouch:Bool = false) {
|
||||
if (_ignoreCursor) {
|
||||
_ignoreCursor = false;
|
||||
|
|
@ -229,6 +242,221 @@ class CameraController extends Object {
|
|||
camera.up.z = 1;
|
||||
}
|
||||
|
||||
function doSpectateCamera(currentTime:Float, dt:Float) {
|
||||
var camera = level.scene.camera;
|
||||
|
||||
var lerpt = Math.pow(0.5, dt / 0.032); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
|
||||
|
||||
var cameraPitchDelta = (Key.isDown(Settings.controlsSettings.camBackward) ? 1 : 0)
|
||||
- (Key.isDown(Settings.controlsSettings.camForward) ? 1 : 0)
|
||||
+ Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis);
|
||||
if (Settings.gamepadSettings.invertYAxis)
|
||||
cameraPitchDelta = -cameraPitchDelta;
|
||||
nextCameraPitch += 0.75 * 5 * cameraPitchDelta * dt * Settings.gamepadSettings.cameraSensitivity;
|
||||
var cameraYawDelta = (Key.isDown(Settings.controlsSettings.camRight) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camLeft) ? 1 : 0)
|
||||
+ Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis);
|
||||
if (Settings.gamepadSettings.invertXAxis)
|
||||
cameraYawDelta = -cameraYawDelta;
|
||||
nextCameraYaw += 0.75 * 5 * cameraYawDelta * dt * Settings.gamepadSettings.cameraSensitivity;
|
||||
|
||||
nextCameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, nextCameraPitch));
|
||||
|
||||
CameraYaw = Util.lerp(CameraYaw, nextCameraYaw, lerpt);
|
||||
CameraPitch = Util.lerp(CameraPitch, nextCameraPitch, lerpt);
|
||||
|
||||
CameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, CameraPitch)); // Util.clamp(CameraPitch, -Math.PI / 12, Math.PI / 2);
|
||||
|
||||
function getRotQuat(v1:Vector, v2:Vector) {
|
||||
function orthogonal(v:Vector) {
|
||||
var x = Math.abs(v.x);
|
||||
var y = Math.abs(v.y);
|
||||
var z = Math.abs(v.z);
|
||||
var other = x < y ? (x < z ? new Vector(1, 0, 0) : new Vector(0, 0, 1)) : (y < z ? new Vector(0, 1, 0) : new Vector(0, 0, 1));
|
||||
return v.cross(other);
|
||||
}
|
||||
|
||||
var u = v1.normalized();
|
||||
var v = v2.normalized();
|
||||
if (u.multiply(-1).equals(v)) {
|
||||
var q = new Quat();
|
||||
var o = orthogonal(u).normalized();
|
||||
q.x = o.x;
|
||||
q.y = o.y;
|
||||
q.z = o.z;
|
||||
q.w = 0;
|
||||
return q;
|
||||
}
|
||||
var half = u.add(v).normalized();
|
||||
var q = new Quat();
|
||||
q.w = u.dot(half);
|
||||
var vr = u.cross(half);
|
||||
q.x = vr.x;
|
||||
q.y = vr.y;
|
||||
q.z = vr.z;
|
||||
return q;
|
||||
}
|
||||
var orientationQuat = level.getOrientationQuat(currentTime);
|
||||
|
||||
if (spectateMarbleIndex == -1) {
|
||||
var up = new Vector(0, 0, 1);
|
||||
up.transform(orientationQuat.toMatrix());
|
||||
var directionVector = new Vector(1, 0, 0);
|
||||
|
||||
var q1 = new Quat();
|
||||
q1.initRotateAxis(0, 1, 0, CameraPitch);
|
||||
directionVector.transform(q1.toMatrix());
|
||||
q1.initRotateAxis(0, 0, 1, CameraYaw);
|
||||
directionVector.transform(q1.toMatrix());
|
||||
directionVector.transform(orientationQuat.toMatrix());
|
||||
|
||||
var dy = Gamepad.getAxis(Settings.gamepadSettings.moveYAxis) * CameraSpeed * dt;
|
||||
var dx = -Gamepad.getAxis(Settings.gamepadSettings.moveXAxis) * CameraSpeed * dt;
|
||||
|
||||
if (Key.isDown(Settings.controlsSettings.forward)) {
|
||||
dy += CameraSpeed * dt;
|
||||
}
|
||||
if (Key.isDown(Settings.controlsSettings.backward)) {
|
||||
dy -= CameraSpeed * dt;
|
||||
}
|
||||
if (Key.isDown(Settings.controlsSettings.left)) {
|
||||
dx += CameraSpeed * dt;
|
||||
}
|
||||
if (Key.isDown(Settings.controlsSettings.right)) {
|
||||
dx -= CameraSpeed * dt;
|
||||
}
|
||||
|
||||
if (MarbleGame.instance.touchInput.movementInput.pressed) {
|
||||
dx = -MarbleGame.instance.touchInput.movementInput.value.x * CameraSpeed * dt;
|
||||
dy = MarbleGame.instance.touchInput.movementInput.value.y * CameraSpeed * dt;
|
||||
}
|
||||
|
||||
if ((!Util.isTouchDevice() && Key.isDown(Settings.controlsSettings.powerup))
|
||||
|| (Util.isTouchDevice() && MarbleGame.instance.touchInput.powerupButton.pressed)
|
||||
|| Gamepad.isDown(Settings.gamepadSettings.powerup)) {
|
||||
dx *= 2;
|
||||
dy *= 2;
|
||||
}
|
||||
|
||||
if (Key.isPressed(Settings.controlsSettings.blast)
|
||||
|| (MarbleGame.instance.touchInput.blastbutton.pressed)
|
||||
|| Gamepad.isPressed(Settings.gamepadSettings.blast)) {
|
||||
var freeMarbleIndex = -1;
|
||||
|
||||
for (i in 0...level.marbles.length) {
|
||||
var marble = level.marbles[i];
|
||||
@:privateAccess if ((marble.connection != null && !marble.connection.spectator)) {
|
||||
freeMarbleIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spectateMarbleIndex = freeMarbleIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
var sideDir = directionVector.cross(up);
|
||||
|
||||
var moveDir = directionVector.multiply(dy).add(sideDir.multiply(dx));
|
||||
camera.pos.load(camera.pos.add(moveDir));
|
||||
|
||||
camera.up = up;
|
||||
camera.target = camera.pos.add(directionVector);
|
||||
} else {
|
||||
if (Key.isPressed(Settings.controlsSettings.left)) {
|
||||
spectateMarbleIndex = (spectateMarbleIndex - 1 + level.marbles.length) % level.marbles.length;
|
||||
@:privateAccess while (level.marbles[spectateMarbleIndex].connection == null
|
||||
|| level.marbles[spectateMarbleIndex].connection.spectator) {
|
||||
spectateMarbleIndex = (spectateMarbleIndex - 1 + level.marbles.length) % level.marbles.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (Key.isPressed(Settings.controlsSettings.right)) {
|
||||
spectateMarbleIndex = (spectateMarbleIndex + 1 + level.marbles.length) % level.marbles.length;
|
||||
@:privateAccess while (level.marbles[spectateMarbleIndex].connection == null
|
||||
|| level.marbles[spectateMarbleIndex].connection.spectator) {
|
||||
spectateMarbleIndex = (spectateMarbleIndex + 1 + level.marbles.length) % level.marbles.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (Key.isPressed(Settings.controlsSettings.blast)
|
||||
|| (MarbleGame.instance.touchInput.blastbutton.pressed)
|
||||
|| Gamepad.isPressed(Settings.gamepadSettings.blast)) {
|
||||
spectateMarbleIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
var marblePosition = level.marbles[spectateMarbleIndex].getAbsPos().getPosition();
|
||||
var up = new Vector(0, 0, 1);
|
||||
up.transform(orientationQuat.toMatrix());
|
||||
var directionVector = new Vector(1, 0, 0);
|
||||
var cameraVerticalTranslation = new Vector(0, 0, 0.3);
|
||||
|
||||
var q1 = new Quat();
|
||||
q1.initRotateAxis(0, 1, 0, CameraPitch);
|
||||
directionVector.transform(q1.toMatrix());
|
||||
cameraVerticalTranslation.transform(q1.toMatrix());
|
||||
q1.initRotateAxis(0, 0, 1, CameraYaw);
|
||||
directionVector.transform(q1.toMatrix());
|
||||
cameraVerticalTranslation.transform(q1.toMatrix());
|
||||
directionVector.transform(orientationQuat.toMatrix());
|
||||
cameraVerticalTranslation.transform(orientationQuat.toMatrix());
|
||||
camera.up = up;
|
||||
camera.pos = marblePosition.sub(directionVector.multiply(CameraDistance));
|
||||
camera.target = marblePosition.add(cameraVerticalTranslation);
|
||||
|
||||
var closeness = 0.1;
|
||||
var rayCastOrigin = marblePosition.add(level.marbles[spectateMarbleIndex].currentUp.multiply(marble._radius));
|
||||
|
||||
var processedShapes = [];
|
||||
for (i in 0...3) {
|
||||
var rayCastDirection = camera.pos.sub(rayCastOrigin);
|
||||
rayCastDirection = rayCastDirection.add(rayCastDirection.normalized().multiply(2));
|
||||
|
||||
var rayCastLen = rayCastDirection.length();
|
||||
|
||||
var results = level.collisionWorld.rayCast(rayCastOrigin, rayCastDirection.normalized(), rayCastLen);
|
||||
|
||||
var firstHit:octree.IOctreeObject.RayIntersectionData = null;
|
||||
var firstHitDistance = 1e8;
|
||||
for (result in results) {
|
||||
if (!processedShapes.contains(result.object)
|
||||
&& (firstHit == null || (rayCastOrigin.distance(result.point) < firstHitDistance))) {
|
||||
firstHit = result;
|
||||
firstHitDistance = rayCastOrigin.distance(result.point);
|
||||
processedShapes.push(result.object);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstHit != null) {
|
||||
if (firstHitDistance < CameraDistance) {
|
||||
// camera.pos = marblePosition.sub(directionVector.multiply(firstHit.distance * 0.7));
|
||||
var plane = new Plane(firstHit.normal.x, firstHit.normal.y, firstHit.normal.z, firstHit.point.dot(firstHit.normal));
|
||||
var normal = firstHit.normal.multiply(-1);
|
||||
// var position = firstHit.point;
|
||||
|
||||
var projected = plane.project(camera.pos.toPoint());
|
||||
var dist = plane.distance(camera.pos.toPoint());
|
||||
|
||||
if (dist >= closeness)
|
||||
break;
|
||||
|
||||
camera.pos = projected.toVector().add(normal.multiply(-closeness));
|
||||
|
||||
var forwardVec = marblePosition.sub(camera.pos).normalized();
|
||||
var rightVec = camera.up.cross(forwardVec).normalized();
|
||||
var upVec = forwardVec.cross(rightVec);
|
||||
|
||||
camera.target = marblePosition.add(upVec.multiply(0.3));
|
||||
camera.up = upVec;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setPosition(camera.pos.x, camera.pos.y, camera.pos.z);
|
||||
}
|
||||
|
||||
public function update(currentTime:Float, dt:Float) {
|
||||
// camera.position.set(marblePosition.x, marblePosition.y, marblePosition.z).sub(directionVector.clone().multiplyScalar(2.5));
|
||||
// this.level.scene.camera.target = marblePosition.add(cameraVerticalTranslation);
|
||||
|
|
@ -239,6 +467,11 @@ class CameraController extends Object {
|
|||
return;
|
||||
}
|
||||
|
||||
if (spectate) {
|
||||
doSpectateCamera(currentTime, dt);
|
||||
return;
|
||||
}
|
||||
|
||||
var camera = level.scene.camera;
|
||||
|
||||
var lerpt = Math.pow(0.5, dt / 0.032); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
|
||||
|
|
|
|||
|
|
@ -1641,6 +1641,16 @@ class Marble extends GameObject {
|
|||
oldPos = this.collider.transform.getPosition();
|
||||
prevRot = this.getRotationQuat().clone();
|
||||
|
||||
// Handle spectator hacky bullshit
|
||||
if (Net.isMP) {
|
||||
if ((connection != null && connection.spectator) || (connection == null && (Net.hostSpectate || Net.clientSpectate))) {
|
||||
this.collider.transform.setPosition(new Vector(1e8, 1e8, 1e8));
|
||||
this.collisionWorld.updateTransform(this.collider);
|
||||
this.setPosition(1e8, 1e8, 1e8);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if (this.controllable) {
|
||||
for (interior in pathedInteriors) {
|
||||
if (Net.isMP)
|
||||
|
|
|
|||
|
|
@ -540,6 +540,7 @@ class MarbleWorld extends Scheduler {
|
|||
public function start() {
|
||||
Console.log("LEVEL START");
|
||||
restart(this.marble, true);
|
||||
|
||||
for (interior in this.interiors)
|
||||
interior.onLevelStart();
|
||||
for (shape in this.dtsObjects)
|
||||
|
|
|
|||
|
|
@ -107,6 +107,11 @@ class MPPreGameDlg extends GuiControl {
|
|||
spectateBtn.vertSizing = Top;
|
||||
spectateBtn.position = new Vector(190, 394);
|
||||
spectateBtn.extent = new Vector(127, 33);
|
||||
spectateBtn.buttonType = Toggle;
|
||||
spectateBtn.pressedAction = (e) -> {
|
||||
NetCommands.toggleSpectate(Net.isHost ? 0 : Net.clientId);
|
||||
updatePlayerList();
|
||||
}
|
||||
dialogImg.addChild(spectateBtn);
|
||||
|
||||
var serverTitle = new GuiText(markerFelt24);
|
||||
|
|
@ -223,20 +228,23 @@ class MPPreGameDlg extends GuiControl {
|
|||
if (Net.isHost) {
|
||||
playerListArr.push({
|
||||
name: Settings.highscoreName,
|
||||
ready: Net.lobbyHostReady
|
||||
ready: Net.lobbyHostReady,
|
||||
spectate: Net.hostSpectate
|
||||
});
|
||||
}
|
||||
if (Net.isClient) {
|
||||
playerListArr.push({
|
||||
name: Settings.highscoreName,
|
||||
ready: Net.lobbyClientReady
|
||||
ready: Net.lobbyClientReady,
|
||||
spectate: Net.clientSpectate
|
||||
});
|
||||
}
|
||||
if (Net.clientIdMap != null) {
|
||||
for (c => v in Net.clientIdMap) {
|
||||
playerListArr.push({
|
||||
name: v.name,
|
||||
ready: v.lobbyReady
|
||||
ready: v.lobbyReady,
|
||||
spectate: v.spectator
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +257,7 @@ class MPPreGameDlg extends GuiControl {
|
|||
|
||||
playBtn.disabled = !allReady;
|
||||
|
||||
var playerListCompiled = playerListArr.map(player -> player.name);
|
||||
var playerListCompiled = playerListArr.map(player -> player.spectate ? '[S] ${player.name}' : player.name);
|
||||
var playerListStateCompiled = playerListArr.map(player -> player.ready ? "[Ready]" : "[Waiting]");
|
||||
playerListLeft.setTexts(playerListCompiled);
|
||||
playerListRight.setTexts(playerListStateCompiled);
|
||||
|
|
@ -268,7 +276,9 @@ class MPPreGameDlg extends GuiControl {
|
|||
// Make everyone un-lobby ready (again!)
|
||||
for (c in Net.clients) {
|
||||
c.lobbyReady = false;
|
||||
c.spectator = false;
|
||||
}
|
||||
Net.hostSpectate = false;
|
||||
Net.lobbyClientReady = false;
|
||||
Net.lobbyHostReady = false;
|
||||
if (Net.isHost) {
|
||||
|
|
|
|||
|
|
@ -77,11 +77,13 @@ abstract class GameConnection {
|
|||
var platform:NetPlatform;
|
||||
var marbleId:Int;
|
||||
var marbleCatId:Int;
|
||||
var spectator:Bool;
|
||||
|
||||
function new(id:Int) {
|
||||
this.id = id;
|
||||
this.moveManager = new MoveManager(this);
|
||||
this.lobbyReady = false;
|
||||
this.spectator = false;
|
||||
}
|
||||
|
||||
public function ready() {
|
||||
|
|
@ -92,6 +94,10 @@ abstract class GameConnection {
|
|||
lobbyReady = !lobbyReady;
|
||||
}
|
||||
|
||||
public function toggleSpectate() {
|
||||
spectator = !spectator;
|
||||
}
|
||||
|
||||
public function queueMove(m:NetMove) {
|
||||
moveManager.queueMove(m);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,8 @@ class Net {
|
|||
public static var lobbyHostReady:Bool;
|
||||
public static var lobbyClientReady:Bool;
|
||||
public static var hostReady:Bool;
|
||||
public static var hostSpectate:Bool;
|
||||
public static var clientSpectate:Bool;
|
||||
|
||||
static var clientIdAllocs:Int = 1;
|
||||
public static var clientId:Int;
|
||||
|
|
@ -373,6 +375,8 @@ class Net {
|
|||
Net.lobbyHostReady = false;
|
||||
Net.lobbyClientReady = false;
|
||||
Net.hostReady = false;
|
||||
Net.hostSpectate = false;
|
||||
Net.clientSpectate = false;
|
||||
// MultiplayerLevelSelectGui.custSelected = false;
|
||||
}
|
||||
if (Net.isHost) {
|
||||
|
|
@ -394,6 +398,8 @@ class Net {
|
|||
Net.lobbyHostReady = false;
|
||||
Net.lobbyClientReady = false;
|
||||
Net.hostReady = false;
|
||||
Net.hostSpectate = false;
|
||||
Net.clientSpectate = false;
|
||||
// MultiplayerLevelSelectGui.custSelected = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -629,6 +635,7 @@ class Net {
|
|||
b.writeByte(v.platform);
|
||||
b.writeByte(v.marbleId);
|
||||
b.writeByte(v.marbleCatId);
|
||||
b.writeByte(v.spectator ? 1 : 0);
|
||||
var name = v.getName();
|
||||
b.writeByte(name.length);
|
||||
for (i in 0...name.length) {
|
||||
|
|
@ -641,6 +648,7 @@ class Net {
|
|||
b.writeByte(getPlatform());
|
||||
b.writeByte(Settings.optionsSettings.marbleIndex);
|
||||
b.writeByte(Settings.optionsSettings.marbleCategoryIndex);
|
||||
b.writeByte(Net.hostSpectate ? 1 : 0);
|
||||
var name = Settings.highscoreName;
|
||||
b.writeByte(name.length);
|
||||
for (i in 0...name.length) {
|
||||
|
|
@ -752,6 +760,7 @@ class Net {
|
|||
var platform = input.readByte();
|
||||
var marble = input.readByte();
|
||||
var marbleCat = input.readByte();
|
||||
var cspectator = input.readByte() == 1;
|
||||
if (id != 0 && id != Net.clientId && !clientIdMap.exists(id)) {
|
||||
Console.log('Adding ghost connection ${id}');
|
||||
addGhost(id);
|
||||
|
|
@ -767,9 +776,11 @@ class Net {
|
|||
clientIdMap[id].setMarbleId(marble, marbleCat);
|
||||
clientIdMap[id].lobbyReady = cready;
|
||||
clientIdMap[id].platform = platform;
|
||||
clientIdMap[id].spectator = cspectator;
|
||||
}
|
||||
if (Net.clientId == id) {
|
||||
Net.lobbyClientReady = cready;
|
||||
Net.clientSpectate = cspectator;
|
||||
}
|
||||
}
|
||||
if (MarbleGame.canvas.content is MPPlayMissionGui) {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,26 @@ class NetCommands {
|
|||
}
|
||||
}
|
||||
|
||||
@:rpc(client) public static function toggleSpectate(clientId:Int) {
|
||||
if (Net.isHost) {
|
||||
if (clientId == 0)
|
||||
Net.hostSpectate = !Net.hostSpectate;
|
||||
else
|
||||
Net.clientIdMap[clientId].toggleSpectate();
|
||||
|
||||
if (MarbleGame.canvas.content is MPPlayMissionGui) {
|
||||
cast(MarbleGame.canvas.content, MPPlayMissionGui).updateLobbyNames();
|
||||
}
|
||||
if (MarbleGame.canvas.children[MarbleGame.canvas.children.length - 1] is MPPreGameDlg) {
|
||||
cast(MarbleGame.canvas.children[MarbleGame.canvas.children.length - 1], MPPreGameDlg).updatePlayerList();
|
||||
}
|
||||
var b = Net.sendPlayerInfosBytes();
|
||||
for (cc in Net.clients) {
|
||||
cc.sendBytes(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@:rpc(client) public static function clientIsReady(clientId:Int) {
|
||||
if (Net.isHost) {
|
||||
if (Net.serverInfo.state == "WAITING") {
|
||||
|
|
@ -214,6 +234,12 @@ class NetCommands {
|
|||
MarbleGame.instance.world.setCursorLock(true);
|
||||
MarbleGame.instance.world.marble.camera.stopOverview();
|
||||
}
|
||||
|
||||
if (Net.clientSpectate || Net.hostSpectate) {
|
||||
MarbleGame.instance.world.marble.camera.enableSpectate();
|
||||
} else {
|
||||
MarbleGame.instance.world.marble.camera.stopSpectate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue