Hello question, I’m trying to setup a game where you go from main menu to a multiplayer scene and a ability to go back to main menu. Currently the room transitions work, but I ran into a problem where if I go from multiplayer game to main menu and back into multiplayer game again I get duplicate characters that I can control. If I repeat the process I will keep duplicating the characters. Currently I’m basing my project based of the NetworkingPlayer sample and if I use the sample it seems to be happening there as well. Any help is much appreciated and some sample code below:
Scene is based of NetworkingPlayer, using SyncRoom, PlayerSync, & SpawnSpotHandler
Scene switching:
public goToScene(index: number) {
this.switcher ??= this.getSceneSwitcher();
this.switcher?.select(index);
}
PlayerMovement
import { Behaviour, PlayerState, SyncedTransform, serializable, Mathf } from "@needle-tools/engine";
class InputValue {
value: number = 0;
}
export class PlayerMovement extends Behaviour {
@serializable(PlayerState)
playerState?: PlayerState;
@serializable(SyncedTransform)
syncedTransform?: SyncedTransform;
@serializable()
speed: number = 1;
private dir: number = 1;
private inputs: Json;
start() {
if (!this.playerState) return;
if (this.playerState.hasOwner)
this.init();
else
this.playerState.onFirstOwnerChangeEvent.addEventListener(this.init);
}
onDestroy() {
this.playerState.onFirstOwnerChangeEvent.removeAllEventListeners();
}
init() {
if (!this.playerState) {
return;
}
// Synced transform synchronizes position, rotation and scale. But has to be manually enabled to determine who the owner is.
if (this.syncedTransform && this.playerState?.isLocalPlayer) {
this.syncedTransform.requestOwnership();
}
this.inputs = {
horizontal: new InputValue(),
vertical: new InputValue(),
};
// Ensure touch actions don't accidentally scroll/refresh the page
this.context.domElement.style.userSelect = "none";
this.context.domElement.style.touchAction = "none";
this.context.renderer.domElement.style.touchAction = "none";
// Prevent iOS magnifying glass and accidental text selection
const preventDefault = (event: Event) => {
event.preventDefault();
};
// Add event listeners to the elements
this.context.domElement.addEventListener('touchstart', preventDefault, { passive: false });
this.context.domElement.addEventListener('touchmove', preventDefault, { passive: false });
this.context.renderer.domElement.addEventListener('touchstart', preventDefault, { passive: false });
this.context.renderer.domElement.addEventListener('touchmove', preventDefault, { passive: false });
}
lateUpdate() {
if (!this.playerState?.isLocalPlayer) return;
if (!this.playerState.hasOwner) return;
const moveAmount = this.speed * this.context.time.deltaTime;
const pos = this.gameObject.transform.position;
let haveMovement = false;
this.inputs.horizontal.value = 0;
this.inputs.vertical.value = 0;
this.handleKeyboard();
if (this.context.input.getTouchesPressedCount() == 1) {
this.handlePointer();
}
this.inputs.horizontal.value = Mathf.clamp(this.inputs.horizontal.value, -1, 1);
this.inputs.vertical.value = Mathf.clamp(this.inputs.vertical.value, -1, 1);
// Right is negative for whatever reason
pos.x -= this.inputs.horizontal.value * moveAmount;
pos.y += this.inputs.vertical.value * moveAmount;
haveMovement = this.inputs.horizontal.value != 0 || this.inputs.vertical.value != 0;
if (haveMovement)
this.dir = this.inputs.horizontal.value < 0 ? -1 : 1;
}
private handleKeyboardAxis(negative: string, positive: string, value: InputValue) {
let offset = 0;
if (this.context.input.isKeyPressed(negative)) {
offset -= 1;
}
if (this.context.input.isKeyPressed(positive)) {
offset += 1;
}
value.value += offset;
}
private handleKeyboard() {
this.handleKeyboardAxis("ArrowLeft", "ArrowRight", this.inputs.horizontal);
this.handleKeyboardAxis("ArrowDown", "ArrowUp", this.inputs.vertical);
// TODO input should be handled using .code instead of .keyCode, then we can support french/belgian layouts as well easily
this.handleKeyboardAxis("a", "d", this.inputs.horizontal);
this.handleKeyboardAxis("s", "w", this.inputs.vertical);
}
// very basic edge-of-screen implementation
private handlePointer() {
if (this.context.input.mousePressed) {
const screenPos = this.context.input.mousePositionRC;
if (screenPos.x < -0.25) {
this.inputs.horizontal.value -= 1;
}
if (screenPos.x > 0.25) {
this.inputs.horizontal.value += 1;
}
if (screenPos.y < -0.5) {
this.inputs.vertical.value -= 1;
}
if (screenPos.y > 0.5) {
this.inputs.vertical.value += 1;
}
}
}
}