Very early & WIP version of multiplayer

This commit is contained in:
Lncvrt committed 2026-03-04 22:32:48 -07:00
1 parent 541b8317c7
commit ec88e92014
29 files changed
+10951 -6

No files matched your search

@@ -0,0 +1,528 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using MikeSchweitzer.WebSocket;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class MultiplayerGamePlayer : MonoBehaviour {
public static MultiplayerGamePlayer Instance;
internal BigInteger score;
private BigInteger attempts;
private float boostLeft;
private float slownessLeft;
[SerializeField] private TMP_Text scoreText;
[SerializeField] private TMP_Text boostText;
[SerializeField] private GameObject pausePanel;
[SerializeField] internal AudioSource backgroundMusic;
[SerializeField] private GameObject birdsParent;
[SerializeField] private GameObject berryParent;
[SerializeField] private GameObject mobileButtons;
[SerializeField] private Button pauseButton;
[SerializeField] private Button restartButton;
[SerializeField] private Button jumpButton;
[SerializeField] private Button rightButton;
[SerializeField] private Button leftButton;
private bool disconnecting = false;
[SerializeField] private TMP_Text pingText;
private readonly List<MultiplayerBerry> berries = new();
private readonly List<MultiplayerUser> players = new();
private float lastMoveSentTime = -1f;
private Image bird;
[SerializeField] private GameObject birdTemplate;
[SerializeField] private GameObject berryTemplate;
private Rigidbody2D rb;
[SerializeField] private RectTransform canvasRect;
[SerializeField] private BoxCollider2D groundCollider;
private UnityEngine.Vector2 velocityBeforePause = new();
internal bool IsGrounded() => Physics2D.Distance(bird.GetComponent<CircleCollider2D>(), groundCollider).distance <= 0.1f;
void Awake() {
Instance = this;
bird = Instantiate(birdTemplate).GetComponent<Image>();
rb = bird.GetComponent<Rigidbody2D>();
bird.transform.SetParent(birdsParent.transform, false);
bird.name = "Bird_" + BazookaManager.Instance.GetAccountID().ToString();
bird.gameObject.SetActive(true);
}
void Start() {
MultiplayerGameTempData multiplayerGameTempData = FindObjectsByType<MultiplayerGameTempData>(FindObjectsSortMode.None)[0];
if (multiplayerGameTempData == null) {
SceneManager.LoadScene("MainMenu");
return;
}
foreach (var player in multiplayerGameTempData.players) {
var birdCopy = Instantiate(birdTemplate);
Destroy(birdCopy.GetComponent<Rigidbody2D>());
Destroy(birdCopy.GetComponent<CircleCollider2D>());
birdCopy.AddComponent<MultiplayerSmoothMove>();
birdCopy.transform.SetParent(birdsParent.transform, false);
birdCopy.name = "Bird_" + player.ID;
birdCopy.SetActive(true);
MultiplayerUser playerToInsert = new() {
ID = player.ID,
Username = player.Username,
Admin = player.Admin,
JoinedAt = player.JoinedAt,
};
players.Add(playerToInsert);
Tools.LoadIcon(
birdCopy.GetComponent<Image>(),
birdCopy.transform.GetChild(0).GetComponent<Image>(),
player.IconData.Icon,
player.IconData.Overlay,
player.IconData.IconColor,
player.IconData.OverlayColor,
player.IconData.CustomIcon,
player.UserID
);
}
Destroy(multiplayerGameTempData.gameObject);
var backgroundColor = BazookaManager.Instance.GetColorSettingBackground();
Camera.main.backgroundColor = new Color(
int.Parse(backgroundColor[0].ToString()) / 255f,
int.Parse(backgroundColor[1].ToString()) / 255f,
int.Parse(backgroundColor[2].ToString()) / 255f
);
Tools.LoadIcon(
bird,
bird.transform.GetChild(0).GetComponent<Image>(),
BazookaManager.Instance.GetBirdIcon(),
BazookaManager.Instance.GetBirdOverlay(),
BazookaManager.Instance.GetColorSettingIcon(),
BazookaManager.Instance.GetColorSettingOverlay(),
BazookaManager.Instance.GetCustomBirdIconData().Selected,
BazookaManager.Instance.GetAccountID() ?? 0
);
UnityEngine.InputSystem.EnhancedTouch.EnhancedTouchSupport.Enable();
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
backgroundMusic.volume = BazookaManager.Instance.GetSettingMusicVolume();
if (Application.isMobilePlatform) mobileButtons.SetActive(true);
UpdateStats(0, 1);
WebsocketHandler.Instance._Connection.StateChanged += OnStateChanged;
WebsocketHandler.Instance._Connection.MessageReceived += OnMessageReceived;
}
private void OnStateChanged(WebSocketConnection connection, WebSocketState oldState, WebSocketState newState) {
if (newState != WebSocketState.DisconnectedFromAppQuit) Disconnect();
}
private void OnMessageReceived(WebSocketConnection connection, WebSocketMessage message) {
Debug.Log(message.String);
if (message.String != null && connection.State == WebSocketState.Connected) {
JToken data = JToken.Parse(message.String);
if (data["type"] != null) {
if (data["type"].ToString() == "multiplayer_berry_spawn") {
string berryId = data["id"].ToString();
string berryType = data["berryType"].ToString();
float posX = float.Parse(data["posX"].ToString());
long timestamp = long.Parse(data["timestamp"].ToString());
MultiplayerBerry berry = new() {
ID = berryId,
Type = berryType,
PosX = posX,
Timestamp = timestamp,
Spawned = false
};
berries.Add(berry);
} else if (data["type"].ToString() == "multiplayer_player_join") {
var userFromResponse = data.ToObject<MultiplayerPlayerFromResponse>();
var birdCopy = Instantiate(birdTemplate);
Destroy(birdCopy.GetComponent<Rigidbody2D>());
Destroy(birdCopy.GetComponent<CircleCollider2D>());
birdCopy.AddComponent<MultiplayerSmoothMove>();
birdCopy.transform.SetParent(birdsParent.transform, false);
birdCopy.name = "Bird_" + userFromResponse.ID;
birdCopy.SetActive(true);
MultiplayerUser player = new() {
ID = userFromResponse.ID,
Username = userFromResponse.Username,
Admin = userFromResponse.Admin,
JoinedAt = userFromResponse.JoinedAt,
};
players.Add(player);
Tools.LoadIcon(
birdCopy.GetComponent<Image>(),
birdCopy.transform.GetChild(0).GetComponent<Image>(),
userFromResponse.IconData.Icon,
userFromResponse.IconData.Overlay,
userFromResponse.IconData.IconColor,
userFromResponse.IconData.OverlayColor,
userFromResponse.IconData.CustomIcon,
userFromResponse.UserID
);
} else if (data["type"].ToString() == "multiplayer_player_leave") {
var userBird = GameObject.Find("Bird_" + data["id"].ToString());
if (userBird) Destroy(userBird);
} else if (data["type"].ToString() == "multiplayer_player_move") {
float x = float.Parse(data["x"].ToString());
float y = float.Parse(data["y"].ToString());
bool flipped = bool.Parse(data["f"].ToString());
var userBird = GameObject.Find("Bird_" + data["id"].ToString());
if (userBird) {
float worldX = x * (canvasRect.rect.width / 2f);
float worldY = y * (canvasRect.rect.height / 2f);
userBird.GetComponent<MultiplayerSmoothMove>().UpdatePosition(worldX, worldY);
userBird.transform.localScale = new UnityEngine.Vector3(flipped ? -1f : 1f, 1f, 1f);
}
}
}
}
}
void OnDestroy() {
WebsocketHandler.Instance._Connection.StateChanged -= OnStateChanged;
WebsocketHandler.Instance._Connection.MessageReceived -= OnMessageReceived;
}
void MoveBird() {
var startPos = bird.transform.position;
float speedModifier = 1f;
bool doMoveRight = false;
bool doMoveLeft = false;
bool doJump = false;
bool doRestart = false;
bool doBack = false;
if (boostLeft > 0f) speedModifier = 1.39f;
else if (slownessLeft > 0f) speedModifier = 0.56f;
bool controllerLeft = Gamepad.current != null && (Gamepad.current.leftStick.left.isPressed || Gamepad.current.dpad.left.isPressed || Gamepad.current.rightStick.left.isPressed);
bool controllerRight = Gamepad.current != null && (Gamepad.current.leftStick.right.isPressed || Gamepad.current.dpad.right.isPressed || Gamepad.current.rightStick.right.isPressed);
bool controllerJump = Gamepad.current != null && (Gamepad.current.leftStick.up.isPressed || Gamepad.current.leftStick.down.isPressed || Gamepad.current.dpad.up.isPressed || Gamepad.current.dpad.down.isPressed || Gamepad.current.rightStick.up.isPressed || Gamepad.current.rightStick.down.isPressed);
if (!Application.isMobilePlatform) {
if (controllerLeft || Keyboard.current.leftArrowKey.isPressed || Keyboard.current.aKey.isPressed || Keyboard.current.jKey.isPressed) {
doMoveLeft = true;
}
if (controllerRight || Keyboard.current.rightArrowKey.isPressed || Keyboard.current.dKey.isPressed || Keyboard.current.lKey.isPressed) {
doMoveRight = true;
}
if (controllerJump || Keyboard.current.spaceKey.isPressed || Keyboard.current.upArrowKey.isPressed || Keyboard.current.wKey.isPressed || Keyboard.current.downArrowKey.isPressed || Keyboard.current.sKey.isPressed || Keyboard.current.kKey.isPressed || Keyboard.current.iKey.isPressed || Mouse.current.leftButton.isPressed || (Gamepad.current != null && Gamepad.current.buttonSouth.isPressed)) {
doJump = true;
}
if (Keyboard.current.rKey.isPressed) {
doRestart = true;
}
} else {
var touches = UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches;
for (int i = 0; i < touches.Count; i++) {
var pos = touches[i].screenPosition;
UnityEngine.Vector3 clickPosition = Camera.main.ScreenToWorldPoint(new UnityEngine.Vector3(pos.x, pos.y, 0f));
clickPosition.z = 0f;
if (leftButton.GetComponent<HoldableButton>().isPressed) doMoveLeft = true;
if (rightButton.GetComponent<HoldableButton>().isPressed) doMoveRight = true;
if (jumpButton.GetComponent<HoldableButton>().isPressed) doJump = true;
if (restartButton.GetComponent<HoldableButton>().isPressed) doRestart = true;
if (pauseButton.GetComponent<HoldableButton>().isPressed) doBack = true;
}
}
if (doMoveLeft && !doMoveRight) {
bird.transform.position += new UnityEngine.Vector3(-(2.4f * (speedModifier * canvasRect.lossyScale.x)), 0f, 0f);
ClampPosition(bird);
bird.transform.localScale = new UnityEngine.Vector3(1f, 1f, 1f);
}
if (doMoveRight && !doMoveLeft) {
bird.transform.position += new UnityEngine.Vector3(2.4f * (speedModifier * canvasRect.lossyScale.x), 0f, 0f);
ClampPosition(bird);
bird.transform.localScale = new UnityEngine.Vector3(-1f, 1f, 1f);
}
if (doMoveLeft || doMoveRight) groundCollider.transform.position = new(bird.transform.position.x, groundCollider.transform.position.y);
if (doJump && IsGrounded()) {
AudioSource.PlayClipAtPoint(Resources.Load<AudioClip>("Sounds/Jump"), Camera.main.transform.position, 0.75f * BazookaManager.Instance.GetSettingSFXVolume());
if (boostLeft > 0f) rb.linearVelocity = new UnityEngine.Vector2(rb.linearVelocity.x, 480f * canvasRect.lossyScale.y);
else if (slownessLeft > 0f) rb.linearVelocity = new UnityEngine.Vector2(rb.linearVelocity.x, 300f * canvasRect.lossyScale.y);
else rb.linearVelocity = new UnityEngine.Vector2(rb.linearVelocity.x, 380f * canvasRect.lossyScale.y);
}
if (doBack) {
TogglePause();
}
if (doRestart) {
if (score != 0) Respawn();
}
if (startPos != bird.transform.position || rb.linearVelocity.y != 0.0f) {
if (Time.realtimeSinceStartup - lastMoveSentTime >= 0.05f) {
lastMoveSentTime = Time.realtimeSinceStartup;
MultiplayerPlayerMove msg = new() {
x = bird.GetComponent<RectTransform>().anchoredPosition.x / (canvasRect.rect.width / 2f),
y = bird.GetComponent<RectTransform>().anchoredPosition.y / (canvasRect.rect.height / 2f),
f = bird.transform.localScale.x == -1f
};
WebsocketHandler.Instance._Connection.AddOutgoingMessage(JsonUtility.ToJson(msg));
}
}
}
void ClampPosition(Image image) {
RectTransform birdRect = image.GetComponent<RectTransform>();
float halfW = canvasRect.rect.width / 2f;
float halfBirdW = birdRect.rect.width / 2f;
UnityEngine.Vector2 pos = birdRect.anchoredPosition;
pos.x = Mathf.Clamp(pos.x, -halfW + halfBirdW, halfW - halfBirdW);
birdRect.anchoredPosition = pos;
}
void FixedUpdate() {
if (disconnecting) return;
if (WebsocketHandler.Instance != null) {
var ping = WebsocketHandler.Instance.GetPing();
if (ping == -1f) pingText.text = "";
else pingText.text = $"Ping: {Math.Floor(ping)}m";
}
if (!pausePanel.activeSelf) {
MoveBird();
if (boostLeft > 0f) {
boostLeft -= Time.deltaTime;
boostText.text = "Boost expires in " + string.Format("{0:0.0}", boostLeft) + "s";
} else if (slownessLeft > 0f) {
slownessLeft -= Time.deltaTime;
boostText.text = "Slowness expires in " + string.Format("{0:0.0}", slownessLeft) + "s";
} else {
boostText.text = "";
}
}
}
void Update() {
if (!pausePanel.activeSelf && rb.gravityScale != 100f * canvasRect.lossyScale.y) rb.gravityScale = 100f * canvasRect.lossyScale.y;
ProcessSpawns();
GameObject[] allBerries = GameObject.FindGameObjectsWithTag("NormalBerry")
.Concat(GameObject.FindGameObjectsWithTag("PoisonBerry"))
.Concat(GameObject.FindGameObjectsWithTag("SlowBerry"))
.Concat(GameObject.FindGameObjectsWithTag("UltraBerry"))
.Concat(GameObject.FindGameObjectsWithTag("SpeedyBerry"))
.Concat(GameObject.FindGameObjectsWithTag("CoinBerry"))
.Concat(GameObject.FindGameObjectsWithTag("RandomBerry"))
.Concat(GameObject.FindGameObjectsWithTag("AntiBerry"))
.Concat(GameObject.FindGameObjectsWithTag("GoldenBerry"))
.ToArray();
foreach (GameObject berry in allBerries) {
if (berry.GetComponent<RectTransform>().anchoredPosition.y < -(canvasRect.rect.height / 2) - berry.GetComponent<RectTransform>().rect.height) Destroy(berry);
else if (!pausePanel.activeSelf && Physics2D.Distance(bird.GetComponent<CircleCollider2D>(), berry.GetComponent<CircleCollider2D>()).distance <= 0.1f) {
switch (berry.tag) {
case "NormalBerry":
DoNormalBerry(berry);
break;
case "PoisonBerry":
DoPoisonBerry();
break;
case "SlowBerry":
DoSlowBerry(berry);
break;
case "UltraBerry":
DoUltraBerry(berry);
break;
case "RandomBerry":
DoRandomBerry(berry);
break;
}
}
if (berry != null) berry.GetComponent<Rigidbody2D>().linearVelocity = new UnityEngine.Vector2(0f, -200f * canvasRect.lossyScale.y);
}
if ((Application.platform == RuntimePlatform.Android && Keyboard.current.escapeKey.wasPressedThisFrame) || !Application.isMobilePlatform && (Keyboard.current.escapeKey.wasPressedThisFrame || (Gamepad.current != null && (Gamepad.current.startButton.wasPressedThisFrame || Gamepad.current.buttonEast.wasPressedThisFrame)))) TogglePause();
}
void Respawn() {
bird.GetComponent<RectTransform>().anchoredPosition = new UnityEngine.Vector3(0f, 64f, 0f);
groundCollider.GetComponent<RectTransform>().anchoredPosition = new(0f, -2.5f);
bird.transform.localScale = new UnityEngine.Vector3(1f, 1f, 1f);
rb.linearVelocity = new();
score = 0;
boostLeft = 0f;
slownessLeft = 0f;
UpdateStats(0, 1);
GameObject[] allBerries = GameObject.FindGameObjectsWithTag("NormalBerry")
.Concat(GameObject.FindGameObjectsWithTag("PoisonBerry"))
.Concat(GameObject.FindGameObjectsWithTag("SlowBerry"))
.Concat(GameObject.FindGameObjectsWithTag("UltraBerry"))
.Concat(GameObject.FindGameObjectsWithTag("RandomBerry"))
.ToArray();
foreach (GameObject berry in allBerries) Destroy(berry);
}
void UpdateStats(BigInteger scoreAddAmount, BigInteger attemptAddAmount) {
score += scoreAddAmount;
attempts += attemptAddAmount;
scoreText.text = $"Score: {Tools.FormatWithCommas(score)} \\u2022 Attempts: {Tools.FormatWithCommas(attempts)}";
if (Application.isMobilePlatform) restartButton.interactable = score != 0;
if (DiscordRPCHandler.Instance != null)
DiscordRPCHandler.Instance.UpdateRPC("Playing in multiplayer mode", $"Score: {Tools.FormatWithCommas(score)}");
}
internal void TogglePause() {
if (pausePanel.activeSelf) DisablePause();
else EnablePause();
}
internal void EnablePause() {
velocityBeforePause = rb.linearVelocity;
rb.linearVelocity = new();
rb.gravityScale = 0f;
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
backgroundMusic.GetComponent<GameMusicHandler>().PauseMusic();
pausePanel.SetActive(true);
var sr = bird.GetComponent<Image>();
var color = sr.color;
color.a = 0.25f;
sr.color = color;
}
internal void DisablePause() {
rb.gravityScale = 100f * canvasRect.lossyScale.y;
rb.linearVelocity = velocityBeforePause;
velocityBeforePause = new();
if (MultiplayerGamePlayerPauseMenu.Instance.editingUI == true) {
MultiplayerGamePlayerPauseMenu.Instance.ToggleEditingUI();
return;
}
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
backgroundMusic.GetComponent<GameMusicHandler>().ResumeMusic();
pausePanel.SetActive(false);
var sr = bird.GetComponent<Image>();
var color = sr.color;
color.a = 1f;
sr.color = color;
}
void OnApplicationPause(bool pause) {
if (pause) EnablePause();
}
void OnApplicationQuit() {
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
void DoNormalBerry(GameObject berry) {
AudioSource.PlayClipAtPoint(Resources.Load<AudioClip>("Sounds/Eat"), Camera.main.transform.position, 1.2f * BazookaManager.Instance.GetSettingSFXVolume());
RemoveBerry(berry);
UpdateStats(1, 0);
}
void DoPoisonBerry() {
AudioSource.PlayClipAtPoint(Resources.Load<AudioClip>("Sounds/Death"), Camera.main.transform.position, 1.2f * BazookaManager.Instance.GetSettingSFXVolume());
Respawn();
UpdateStats(0, 0);
}
void DoSlowBerry(GameObject berry) {
AudioSource.PlayClipAtPoint(Resources.Load<AudioClip>("Sounds/Downgrade"), Camera.main.transform.position, 0.35f * BazookaManager.Instance.GetSettingSFXVolume());
RemoveBerry(berry);
boostLeft = 0f;
slownessLeft = 10f;
if (score > 0) {
UpdateStats(-1, 0);
}
}
void DoUltraBerry(GameObject berry) {
AudioSource.PlayClipAtPoint(Resources.Load<AudioClip>("Sounds/Powerup"), Camera.main.transform.position, 0.35f * BazookaManager.Instance.GetSettingSFXVolume());
RemoveBerry(berry);
if (slownessLeft > 0f) {
slownessLeft = 0f;
UpdateStats(1, 0);
} else {
boostLeft += 10f;
UpdateStats(5, 0);
}
}
void DoRandomBerry(GameObject berry) {
Action[] funcs = {
() => DoNormalBerry(berry),
() => DoSlowBerry(berry),
() => DoUltraBerry(berry)
};
funcs[UnityEngine.Random.Range(0, funcs.Length)]();
}
public async void Disconnect() {
if (disconnecting) return;
disconnecting = true;
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
if (WebsocketHandler.Instance._Connection.State == WebSocketState.Connected) {
MultiplayerLeaveMessage msg = new();
WebsocketHandler.Instance._Connection.AddOutgoingMessage(JsonUtility.ToJson(msg));
}
await SceneManager.LoadSceneAsync("PlayMenu");
}
void ProcessSpawns() {
foreach (var berry in berries) {
if (berry.Spawned) continue;
berry.Spawned = true;
Image newBerry = Instantiate(berryTemplate).GetComponent<Image>();
newBerry.transform.SetParent(berryParent.transform, false);
if (berry.Type == "NormalBerry") {
newBerry.sprite = Resources.Load<Sprite>("Berries/Berry");
newBerry.tag = "NormalBerry";
} else if (berry.Type == "PoisonBerry") {
newBerry.sprite = Resources.Load<Sprite>("Berries/PoisonBerry");
newBerry.tag = "PoisonBerry";
} else if (berry.Type == "SlowBerry") {
newBerry.sprite = Resources.Load<Sprite>("Berries/SlowBerry");
newBerry.tag = "SlowBerry";
} else if (berry.Type == "UltraBerry") {
newBerry.sprite = Resources.Load<Sprite>("Berries/UltraBerry");
newBerry.tag = "UltraBerry";
} else {
newBerry.sprite = Resources.Load<Sprite>("Berries/BerryNoColor");
newBerry.tag = "RandomBerry";
RainbowImage randomBerryRainbowImage = newBerry.gameObject.AddComponent<RainbowImage>();
randomBerryRainbowImage.frequency = 5f;
}
float delayMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - berry.Timestamp;
float spawnY = (canvasRect.rect.height / 2) + newBerry.rectTransform.rect.height;
float distanceTraveled = 200f / 1000f * delayMs;
float y = spawnY - distanceTraveled;
float bottomEdge = -(canvasRect.rect.height / 2) - newBerry.rectTransform.rect.height;
if (y < bottomEdge) {
Destroy(newBerry.gameObject);
continue;
}
float usableWidth = canvasRect.rect.width - newBerry.rectTransform.rect.width;
float worldX = (berry.PosX * usableWidth) - (usableWidth / 2f);
newBerry.GetComponent<RectTransform>().anchoredPosition = new UnityEngine.Vector2(worldX, y);
newBerry.gameObject.SetActive(true);
}
}
void RemoveBerry(GameObject berry) {
Destroy(berry);
berries.RemoveAll(b => b.ID == berry.name[6..]);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7e535d478e6c34e529b9901ba5f8c469
@@ -0,0 +1,127 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class MultiplayerGamePlayerPauseMenu : MonoBehaviour {
public static MultiplayerGamePlayerPauseMenu Instance;
[SerializeField] private Button leaveButton;
[SerializeField] private Button continueButton;
[SerializeField] private Button editUiButton;
[SerializeField] private Button resetUiButton;
[SerializeField] private AudioSource songLoop;
[SerializeField] private TMP_Text scoreText;
[SerializeField] private TMP_Text boostText;
[SerializeField] private Button pauseButton;
[SerializeField] private Button restartButton;
[SerializeField] private Button jumpButton;
[SerializeField] private Button rightButton;
[SerializeField] private Button leftButton;
internal bool editingUI = false;
[SerializeField] private Toggle settingFullscreenToggle;
[SerializeField] private Toggle settingVSyncToggle;
[SerializeField] private Toggle settingRandomMusicToggle;
[SerializeField] private Slider musicSlider;
[SerializeField] private Slider sfxSlider;
[SerializeField] private TMP_Text musicSliderText;
[SerializeField] private TMP_Text sfxSliderText;
void Awake() {
Instance = this;
leaveButton.onClick.AddListener(MultiplayerGamePlayer.Instance.Disconnect);
continueButton.onClick.AddListener(MultiplayerGamePlayer.Instance.DisablePause);
musicSlider.onValueChanged.AddListener(value => {
BazookaManager.Instance.SetSettingMusicVolume(value);
songLoop.volume = value;
});
sfxSlider.onValueChanged.AddListener(value => {
BazookaManager.Instance.SetSettingSFXVolume(value);
});
editUiButton.onClick.AddListener(() => {
ToggleEditingUI();
});
resetUiButton.onClick.AddListener(() => {
scoreText.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -70f);
boostText.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(0f, -140f);
PlayerPrefs.DeleteKey("DraggedUIScoreTextCustom");
PlayerPrefs.DeleteKey("DraggedUIBoostTextCustom");
if (Application.isMobilePlatform) {
pauseButton.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(128f, -128f);
restartButton.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(-128f, -128f);
jumpButton.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(-128f, 288f);
rightButton.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(-128f, 128f);
leftButton.transform.GetComponent<RectTransform>().anchoredPosition = new Vector2(128f, 128f);
PlayerPrefs.DeleteKey("DraggedUIPauseButton");
PlayerPrefs.DeleteKey("DraggedUIRestartButton");
PlayerPrefs.DeleteKey("DraggedUIJumpButton");
PlayerPrefs.DeleteKey("DraggedUIRightButton");
PlayerPrefs.DeleteKey("DraggedUILeftButton");
}
});
musicSlider.value = BazookaManager.Instance.GetSettingMusicVolume();
sfxSlider.value = BazookaManager.Instance.GetSettingSFXVolume();
if (!Application.isMobilePlatform) {
settingFullscreenToggle.isOn = BazookaManager.Instance.GetSettingFullScreen() == true;
settingVSyncToggle.isOn = BazookaManager.Instance.GetSettingVsync() == true;
settingRandomMusicToggle.isOn = BazookaManager.Instance.GetSettingRandomMusic() == true;
settingFullscreenToggle.onValueChanged.AddListener(value => {
BazookaManager.Instance.SetSettingFullScreen(value);
var width = Display.main.systemWidth;
var height = Display.main.systemHeight;
Screen.SetResolution(width, height, value);
});
settingVSyncToggle.onValueChanged.AddListener(value => {
BazookaManager.Instance.SetSettingVsync(value);
QualitySettings.vSyncCount = value ? 1 : -1;
});
} else {
settingFullscreenToggle.interactable = false;
settingVSyncToggle.interactable = false;
settingRandomMusicToggle.isOn = BazookaManager.Instance.GetSettingRandomMusic() == true;
}
settingRandomMusicToggle.onValueChanged.AddListener(value => BazookaManager.Instance.SetSettingRandomMusic(value));
musicSlider.onValueChanged.AddListener(value => {
BazookaManager.Instance.SetSettingMusicVolume(value);
MultiplayerGamePlayer.Instance.backgroundMusic.volume = value;
});
sfxSlider.onValueChanged.AddListener(value => BazookaManager.Instance.SetSettingSFXVolume(value));
}
public void ToggleEditingUI() {
editingUI = !editingUI;
musicSlider.gameObject.SetActive(!musicSlider.gameObject.activeSelf);
sfxSlider.gameObject.SetActive(!sfxSlider.gameObject.activeSelf);
musicSliderText.gameObject.SetActive(!musicSliderText.gameObject.activeSelf);
sfxSliderText.gameObject.SetActive(!sfxSliderText.gameObject.activeSelf);
settingFullscreenToggle.gameObject.SetActive(!settingFullscreenToggle.gameObject.activeSelf);
settingVSyncToggle.gameObject.SetActive(!settingVSyncToggle.gameObject.activeSelf);
settingRandomMusicToggle.gameObject.SetActive(!settingRandomMusicToggle.gameObject.activeSelf);
leaveButton.gameObject.SetActive(!leaveButton.gameObject.activeSelf);
continueButton.gameObject.SetActive(!continueButton.gameObject.activeSelf);
editUiButton.transform.GetChild(0).GetComponent<TMP_Text>().text = editUiButton.transform.GetChild(0).GetComponent<TMP_Text>().text == "Edit UI" ? "Done" : "Edit UI";
resetUiButton.gameObject.SetActive(!resetUiButton.gameObject.activeSelf);
scoreText.GetComponent<DraggableUI>().canDrag = !scoreText.GetComponent<DraggableUI>().canDrag;
boostText.GetComponent<DraggableUI>().canDrag = !boostText.GetComponent<DraggableUI>().canDrag;
if (Application.isMobilePlatform) {
var pauseDraggableUI = pauseButton.GetComponent<DraggableUI>();
var restartDraggableUI = restartButton.GetComponent<DraggableUI>();
var jumpDraggableUI = jumpButton.GetComponent<DraggableUI>();
var rightDraggableUI = rightButton.GetComponent<DraggableUI>();
var leftDraggableUI = leftButton.GetComponent<DraggableUI>();
pauseButton.transform.parent.SetSiblingIndex(pauseDraggableUI.canDrag ? 0 : 2);
pauseDraggableUI.canDrag = !pauseDraggableUI.canDrag;
restartDraggableUI.canDrag = !restartDraggableUI.canDrag;
jumpDraggableUI.canDrag = !jumpDraggableUI.canDrag;
rightDraggableUI.canDrag = !rightDraggableUI.canDrag;
leftDraggableUI.canDrag = !leftDraggableUI.canDrag;
pauseButton.interactable = !pauseDraggableUI.canDrag;
restartButton.interactable = !restartDraggableUI.canDrag;
jumpButton.interactable = !jumpDraggableUI.canDrag;
rightButton.interactable = !rightDraggableUI.canDrag;
leftButton.interactable = !leftDraggableUI.canDrag;
jumpButton.transform.GetChild(0).GetComponent<TMP_Text>().color = !jumpDraggableUI.canDrag ? MultiplayerGamePlayer.Instance.IsGrounded() ? Color.white : Color.red : Color.white;
restartButton.interactable = MultiplayerGamePlayer.Instance.score != 0;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b54e7225efaef4c928f41f08c9eb65a0
@@ -0,0 +1,9 @@
using UnityEngine;
public class MultiplayerGameTempData : MonoBehaviour {
public MultiplayerPlayerFromResponse[] players;
void Awake() {
DontDestroyOnLoad(gameObject);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 35c0ea2030f1b4768a7acf8a120a810f
+1 -1
View File
@@ -19,7 +19,7 @@ public class MenuMusic : MonoBehaviour {
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode) {
if (scene.name == "GamePlayer" || scene.name == "CustomGamePlayer" || scene.name == "AutoGamePlayer") {
if (scene.name == "GamePlayer" || scene.name == "CustomGamePlayer" || scene.name == "AutoGamePlayer" || scene.name == "MultiplayerGamePlayer") {
SceneManager.sceneLoaded -= OnSceneLoaded;
Destroy(gameObject);
}
@@ -0,0 +1,40 @@
using UnityEngine;
public class MultiplayerSmoothMove : MonoBehaviour {
private RectTransform rt;
private Vector3 fromPosition;
private Vector3 toPosition;
private Vector3 pendingPosition;
private bool hasPending = false;
private float elapsed = 0f;
private const float interval = 0.1f;
void Awake() {
rt = GetComponent<RectTransform>();
fromPosition = rt.anchoredPosition;
toPosition = rt.anchoredPosition;
}
public void UpdatePosition(float x, float y) {
if (hasPending) {
fromPosition = rt.anchoredPosition;
toPosition = pendingPosition;
elapsed = 0f;
}
pendingPosition = new Vector3(x, y, 0f);
hasPending = true;
}
void Update() {
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / interval);
rt.anchoredPosition = Vector3.Lerp(fromPosition, toPosition, t);
if (t >= 1f && hasPending) {
fromPosition = toPosition;
toPosition = pendingPosition;
hasPending = false;
elapsed = 0f;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b2c747d4079894784b71495d69c4b204
+26
View File
@@ -0,0 +1,26 @@
using UnityEngine;
public class ObjectSpinner : MonoBehaviour {
[SerializeField] private float frequency = 1f;
[SerializeField] private bool spinX = false;
[SerializeField] private bool spinY = true;
[SerializeField] private bool spinZ = false;
[SerializeField] private bool useLocalSpace = true;
[SerializeField] private bool reverseDirection = false;
private float DegreesPerSecond => frequency * 360f;
void Update() {
float direction = reverseDirection ? -1f : 1f;
float delta = DegreesPerSecond * direction * Time.deltaTime;
Vector3 rotationAxis = new(
spinX ? delta : 0f,
spinY ? delta : 0f,
spinZ ? delta : 0f
);
if (useLocalSpace) transform.Rotate(rotationAxis, Space.Self);
else transform.Rotate(rotationAxis, Space.World);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9e1b7788c31484c679f01dbc03c28ee9
+14 -1
View File
@@ -2,6 +2,7 @@ using MikeSchweitzer.WebSocket;
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using System;
public class WebsocketHandler : MonoBehaviour {
public static WebsocketHandler Instance;
@@ -13,6 +14,8 @@ public class WebsocketHandler : MonoBehaviour {
private static readonly WaitForSeconds _waitForSeconds2 = new(2f);
private string lastSceneName = null;
private float _lastPingMs = -1f;
private void Awake() {
if (Instance != null) {
Destroy(gameObject);
@@ -24,10 +27,14 @@ public class WebsocketHandler : MonoBehaviour {
_Connection = gameObject.AddComponent<WebSocketConnection>();
_Connection.DesiredConfig = new WebSocketConfig {
MaxReceiveBytes = 10 * 1024 * 1024
MaxReceiveBytes = 10 * 1024 * 1024,
PingInterval = TimeSpan.FromSeconds(5),
PingMessage = new WebSocketMessage("ping"),
ShouldPingWaitForPong = true,
};
_Connection.StateChanged += OnStateChanged;
_Connection.ErrorMessageReceived += OnErrorMessageReceived;
_Connection.PongReceived += OnPongReceived;
SceneManager.sceneLoaded += OnSceneLoaded;
@@ -90,4 +97,10 @@ public class WebsocketHandler : MonoBehaviour {
}
lastSceneName = scene.name;
}
public float GetPing() => _lastPingMs;
private void OnPongReceived(WebSocketConnection connection, DateTime timestamp) {
_lastPingMs = (float)connection.LastPingPongInterval.TotalMilliseconds;
}
}
+72 -3
View File
@@ -1,4 +1,6 @@
using System;
using MikeSchweitzer.WebSocket;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
@@ -13,6 +15,12 @@ public class PlayMenu : MonoBehaviour {
[SerializeField] private Button customBackButton;
[SerializeField] private Button customPlayButton;
[SerializeField] private Button customNormalizeButton;
[SerializeField] private Button multiplayerButton;
[SerializeField] private GameObject multiplayerJoinStatusPanel;
[SerializeField] private GameObject multiplayerJoiningPanel;
[SerializeField] private GameObject multiplayerDeniedPanel;
[SerializeField] private Button multiplayerDeniedBackButton;
[SerializeField] private TMP_Text multiplayerDeniedReasonText;
[SerializeField] private Button autoButton;
[SerializeField] private Button autoBackButton;
[SerializeField] private Button autoPlayButton;
@@ -180,6 +188,22 @@ public class PlayMenu : MonoBehaviour {
customGameTempData.useAI = useAiToggle.isOn;
await SceneManager.LoadSceneAsync("CustomGamePlayer");
});
multiplayerButton.onClick.AddListener(() => {
selectionMenu.SetActive(false);
multiplayerJoinStatusPanel.SetActive(true);
multiplayerJoiningPanel.SetActive(true);
MultiplayerJoinRequestMessage msg = new() {
auth = BazookaManager.Instance.GetAccountSession()
};
WebsocketHandler.Instance._Connection.AddOutgoingMessage(JsonUtility.ToJson(msg));
});
multiplayerDeniedBackButton.onClick.AddListener(() => {
multiplayerDeniedReasonText.text = "";
multiplayerDeniedPanel.SetActive(false);
multiplayerJoinStatusPanel.SetActive(false);
selectionMenu.SetActive(true);
});
autoButton.onClick.AddListener(async () => {
if (BazookaManager.Instance.GetReadAutoWarning() == true) {
await SceneManager.LoadSceneAsync("AutoGamePlayer");
@@ -215,6 +239,33 @@ public class PlayMenu : MonoBehaviour {
goldenBerryChance.onDeselect.AddListener((value) => OnDeselect(value, goldenBerryChance));
nothingBerryChance.onSelect.AddListener((value) => OnSelect(value, nothingBerryChance));
nothingBerryChance.onDeselect.AddListener((value) => OnDeselect(value, nothingBerryChance));
WebsocketHandler.Instance._Connection.MessageReceived += OnMessageReceived;
}
private void OnMessageReceived(WebSocketConnection connection, WebSocketMessage message) {
if (message.String != null && connection.State == WebSocketState.Connected) {
JToken data = JToken.Parse(message.String);
Debug.Log(data);
if (data["type"] != null) {
if (data["type"].ToString() == "multiplayer_join_response") {
bool accepted = bool.Parse(data["accepted"].ToString());
string denyReason = data["denyReason"].ToString();
MultiplayerPlayerFromResponse[] players = data["players"].ToObject<MultiplayerPlayerFromResponse[]>();
if (accepted) {
GameObject obj = new("MultiplayerGameTempData");
obj.AddComponent<MultiplayerGameTempData>();
MultiplayerGameTempData multiplayerGameTempData = obj.GetComponent<MultiplayerGameTempData>();
multiplayerGameTempData.players = players;
SceneManager.LoadScene("MultiplayerGamePlayer");
} else {
multiplayerJoiningPanel.SetActive(false);
multiplayerDeniedPanel.SetActive(true);
multiplayerDeniedReasonText.text = denyReason;
}
}
}
}
}
void Start() {
@@ -226,10 +277,26 @@ public class PlayMenu : MonoBehaviour {
customButton.interactable = false;
customButton.transform.GetChild(0).GetComponent<TMP_Text>().text += "\n<size=12>You need level 5 or higher to access this</size>";
}
if (Tools.GetLevelInfo().level < 10) {
if (Tools.GetLevelInfo().level < 3) {
autoButton.interactable = false;
autoButton.transform.GetChild(0).GetComponent<TMP_Text>().text += "\n<size=12>You need level 10 or higher to access this</size>";
autoButton.transform.GetChild(0).GetComponent<TMP_Text>().text += "\n<size=12>You need level 3 or higher to access this</size>";
}
if (WebsocketHandler.Instance == null || WebsocketHandler.Instance._Connection.State != WebSocketState.Connected) {
multiplayerButton.gameObject.SetActive(false);
var pos = autoButton.transform.localPosition;
pos.x = 0;
autoButton.transform.localPosition = pos;
} else if (BazookaManager.Instance.GetAccountID() == null || BazookaManager.Instance.GetAccountName() == null || BazookaManager.Instance.GetAccountSession() == null) {
multiplayerButton.interactable = false;
multiplayerButton.transform.GetChild(0).GetComponent<TMP_Text>().text += "\n<size=12>You must be logged to access this</size>";
} else if (Tools.GetLevelInfo().level < 10) {
multiplayerButton.interactable = false;
multiplayerButton.transform.GetChild(0).GetComponent<TMP_Text>().text += "\n<size=12>You need level 10 or higher to access this</size>";
}
}
void OnDestroy() {
WebsocketHandler.Instance._Connection.MessageReceived -= OnMessageReceived;
}
void ValidateTotal() {
@@ -306,7 +373,9 @@ public class PlayMenu : MonoBehaviour {
if (Keyboard.current.escapeKey.wasPressedThisFrame && customBackButton.interactable) {
if (customMenu.activeSelf) customBackButton.onClick.Invoke();
else if (autoWarningMenu.activeSelf) autoBackButton.onClick.Invoke();
else await SceneManager.LoadSceneAsync("MainMenu");
else if (multiplayerJoinStatusPanel.activeSelf) {
if (multiplayerDeniedPanel.activeSelf) multiplayerDeniedBackButton.onClick.Invoke();
} else await SceneManager.LoadSceneAsync("MainMenu");
}
}
}
+7
View File
@@ -0,0 +1,7 @@
public class MultiplayerBerry {
public string ID { get; set; }
public string Type { get; set; }
public float PosX { get; set; }
public long Timestamp { get; set; }
public bool Spawned { get; set; }
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fb1a97212b8704ea0985bec3ed3a0896
@@ -0,0 +1,52 @@
using System.Numerics;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine.Scripting;
[Preserve]
public class MultiplayerPlayerIconData {
[Preserve]
[JsonProperty("icon")]
public int Icon { get; set; }
[Preserve]
[JsonProperty("overlay")]
public int Overlay { get; set; }
[Preserve]
[JsonProperty("iconColor")]
public JArray IconColor { get; set; }
[Preserve]
[JsonProperty("overlayColor")]
public JArray OverlayColor { get; set; }
[Preserve]
[JsonProperty("customIcon")]
public string CustomIcon { get; set; }
}
public class MultiplayerPlayerFromResponse {
[Preserve]
[JsonProperty("id")]
public string ID { get; set; }
[Preserve]
[JsonProperty("userId")]
public BigInteger UserID { get; set; }
[Preserve]
[JsonProperty("username")]
public string Username { get; set; }
[Preserve]
[JsonProperty("admin")]
public bool Admin { get; set; }
[Preserve]
[JsonProperty("timestamp")]
public long JoinedAt { get; set; }
[Preserve]
[JsonProperty("iconData")]
public MultiplayerPlayerIconData IconData { get; set; }
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 202aaab112ee946b1be7b8cc67e05fa1
+6
View File
@@ -0,0 +1,6 @@
public class MultiplayerUser {
public string ID { get; set; }
public string Username { get; set; }
public bool Admin { get; set; }
public long JoinedAt { get; set; }
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a4d4480ccb36440fca48a0444dfc7733
@@ -0,0 +1,8 @@
using System;
[Serializable]
public class MultiplayerJoinRequestMessage {
public string type = "multiplayer_join_request";
public string auth;
public long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 93583d6121d2446faaa728fc4b8358d1
@@ -0,0 +1,7 @@
using System;
[Serializable]
public class MultiplayerLeaveMessage {
public string type = "multiplayer_leave";
public long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6b5d67b79f4574f48870b8187646b6cc
@@ -0,0 +1,10 @@
using System;
[Serializable]
public class MultiplayerPlayerMove {
public string type = "multiplayer_player_move";
public float x;
public float y;
public bool f;
public long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8808768c94bbe4bf099fba481beb0901