Refactoring, Code cleanup, Player Width

main
Benjamin Kraft 1 year ago
parent 2f1d19c094
commit 13d5237c6b
  1. 2
      Assets/Prefabs/Player.prefab
  2. 67
      Assets/Scripts/Dimensions.cs
  3. 115
      Assets/Scripts/Game/AIPlayer.cs
  4. 23
      Assets/Scripts/Game/Ball.cs
  5. 3
      Assets/Scripts/Game/Collectable.cs
  6. 12
      Assets/Scripts/Game/DeathGate.cs
  7. 65
      Assets/Scripts/Game/GameManager.cs
  8. 4
      Assets/Scripts/Game/Modification.cs
  9. 9
      Assets/Scripts/Game/ModificationProperties.cs
  10. 111
      Assets/Scripts/Game/Player.cs
  11. 10
      Assets/Scripts/Game/RealPlayer.cs
  12. 4
      Assets/Scripts/Game/Settings.cs
  13. 10
      Assets/Scripts/Game/Wormhole.cs
  14. 29
      Assets/Scripts/GameUI.cs
  15. 24
      Assets/Scripts/MenuUI.cs
  16. 3
      Assets/Scripts/NetworkCommandLine.cs

@ -113,7 +113,7 @@ Transform:
m_GameObject: {fileID: 5402279313309450415} m_GameObject: {fileID: 5402279313309450415}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 4, y: 1, z: 1} m_LocalScale: {x: 5, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 4352057116839657264} - {fileID: 4352057116839657264}

@ -1,23 +1,24 @@
using System;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization; using UnityEngine.Serialization;
[ExecuteInEditMode] [ExecuteInEditMode]
public class Dimensions : MonoBehaviour { public class Dimensions : MonoBehaviour {
public static Dimensions Singleton;
public bool isUpdating; public bool isUpdating;
[FormerlySerializedAs("panelHeightPixels")] [FormerlySerializedAs("panelHeightPixels")]
[Tooltip("Player panels height")] [Tooltip("Player panels height")]
public float panelHeightPixelsMinimum; public float panelHeightPixelsMinimum;
[Tooltip("Size in Unity units")] [Tooltip("Size in Unity units")]
public Vector2 playGroundSize; public Vector2 playGroundSize;
[Tooltip("Height of the empty space between board and death zone")] [Tooltip("Height of the empty space between board and death zone")]
public float emptySpaceHeight; public float emptySpaceHeight;
public Camera mainCamera; public Camera mainCamera;
public EdgeCollider2D topC, bottomC, leftC, rightC; public EdgeCollider2D topC, bottomC, leftC, rightC;
public SpriteRenderer playGround; public SpriteRenderer playGround;
@ -25,7 +26,14 @@ public class Dimensions : MonoBehaviour {
public SpriteRenderer playerPanelBottom; public SpriteRenderer playerPanelBottom;
public SpriteRenderer background; public SpriteRenderer background;
public static Dimensions Singleton; private LineRenderer topL, bottomL;
public Vector2 PlaySize { get; private set; }
public Vector2 PlaySizeBoards { get; private set; }
public float PanelHeightPixels { get; private set; }
private void Awake() { private void Awake() {
Singleton = this; Singleton = this;
GetComponents(); GetComponents();
@ -34,29 +42,28 @@ public class Dimensions : MonoBehaviour {
isUpdating = false; isUpdating = false;
} }
private void Update() {
if (!isUpdating)
return;
SetDimensions();
}
private void GetComponents() { private void GetComponents() {
topL = topC.GetComponent<LineRenderer>(); topL = topC.GetComponent<LineRenderer>();
bottomL = bottomC.GetComponent<LineRenderer>(); bottomL = bottomC.GetComponent<LineRenderer>();
} }
public Vector2 PlaySize { get; private set; }
public Vector2 PlaySizeBoards { get; private set; }
public float PanelHeightPixels { get; private set; }
private LineRenderer topL, bottomL;
private void SetDimensions() { private void SetDimensions() {
float heightByPanels = 1 / (1 - 2 * panelHeightPixelsMinimum / mainCamera.pixelHeight) * playGroundSize.y; var heightByPanels = 1 / (1 - 2 * panelHeightPixelsMinimum / mainCamera.pixelHeight) * playGroundSize.y;
float heightByPlayground = playGroundSize.x / mainCamera.aspect; var heightByPlayground = playGroundSize.x / mainCamera.aspect;
float height = Mathf.Max(heightByPanels, heightByPlayground); var height = Mathf.Max(heightByPanels, heightByPlayground);
float width = mainCamera.aspect * height; var width = mainCamera.aspect * height;
float panelHeight = (height - playGroundSize.y) / 2; var panelHeight = (height - playGroundSize.y) / 2;
mainCamera.orthographicSize = height / 2; mainCamera.orthographicSize = height / 2;
float top = playGroundSize.y / 2; var top = playGroundSize.y / 2;
float right = playGroundSize.x / 2; var right = playGroundSize.x / 2;
PanelHeightPixels = panelHeight / height * mainCamera.pixelHeight; PanelHeightPixels = panelHeight / height * mainCamera.pixelHeight;
PlaySize = playGroundSize; PlaySize = playGroundSize;
@ -75,13 +82,13 @@ public class Dimensions : MonoBehaviour {
bottomC.points = new[] {v1, v2}; bottomC.points = new[] {v1, v2};
leftC.points = new[] {v3, v4}; leftC.points = new[] {v3, v4};
rightC.points = new[] {v3, v4}; rightC.points = new[] {v3, v4};
playGround.transform.rotation = Quaternion.Euler(0, 0, 90); playGround.transform.rotation = Quaternion.Euler(0, 0, 90);
playGround.size = new Vector2(playGroundSize.y, playGroundSize.x); playGround.size = new Vector2(playGroundSize.y, playGroundSize.x);
const float lineWidth = 0.5f; const float lineWidth = 0.5f;
Vector3 offset = Vector3.up * lineWidth / 2; var offset = Vector3.up * lineWidth / 2;
topL.positionCount = bottomL.positionCount = 2; topL.positionCount = bottomL.positionCount = 2;
var v12 = new[] {(Vector3) v1 - offset, (Vector3) v2 - offset}; var v12 = new[] {(Vector3) v1 - offset, (Vector3) v2 - offset};
topL.SetPositions(v12); topL.SetPositions(v12);
@ -90,15 +97,9 @@ public class Dimensions : MonoBehaviour {
playerPanelTop.transform.position = new Vector2(0, (height + PlaySize.y) / 4); playerPanelTop.transform.position = new Vector2(0, (height + PlaySize.y) / 4);
playerPanelBottom.transform.position = new Vector2(0, -(height + PlaySize.y) / 4); playerPanelBottom.transform.position = new Vector2(0, -(height + PlaySize.y) / 4);
playerPanelTop.size = playerPanelBottom.size = new Vector2(width, panelHeight); playerPanelTop.size = playerPanelBottom.size = new Vector2(width, panelHeight);
background.size = new Vector2(width, playGroundSize.y); background.size = new Vector2(width, playGroundSize.y);
} }
private void Update() {
if (!isUpdating)
return;
SetDimensions();
}
} }

@ -1,15 +1,45 @@
using System;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace Game { namespace Game {
public class AIPlayer : Player { public class AIPlayer : Player {
private const float SmoothTime = 0.2f;
private float currentSmoothV;
private bool isApproaching;
private float lastDirection;
public Difficulty Difficulty { get; set; } public Difficulty Difficulty { get; set; }
private float FutureSeconds => (float) Difficulty * 0.5f; private float FutureSeconds => (float) Difficulty * 0.5f;
private float IdlePosition => Difficulty >= Difficulty.Medium ? 0 : X;
private float DistortAmount => 1 - 1 / ((float) Difficulty + 1);
private void FixedUpdate() {
var dt = Time.fixedDeltaTime;
var target = GetTargetPosition();
var h = Mathf.Max(Speed * dt, Width / 2);
goingLeft = target < X - h;
goingRight = target > X + h;
if (goingLeft || goingRight) {
isApproaching = false;
lastDirection = goingLeft ? -1 : 1;
TryLinearMove(dt);
} else {
if (!isApproaching) {
isApproaching = true;
currentSmoothV = Speed * lastDirection;
}
ApproachPosition(target);
}
}
// True if ball y velocity points towards player // True if ball y velocity points towards player
private bool BallApproaches(Ball ball) { private bool BallApproaches(Ball ball) {
var ballVy = ball.Rb.velocity.y; var ballVy = ball.Rb.velocity.y;
@ -26,8 +56,8 @@ namespace Game {
point = Vector2.zero; point = Vector2.zero;
rs = Vector2.zero; rs = Vector2.zero;
Vector2 m1 = e1 - o1; var m1 = e1 - o1;
Vector2 m2 = e2 - o2; var m2 = e2 - o2;
/* /*
* *
@ -62,12 +92,10 @@ namespace Game {
return false; return false;
} }
private float IdlePosition => Difficulty >= Difficulty.Medium ? 0 : X;
// TODO Also must include fact that players have a height, maybe check the incoming angle // TODO Also must include fact that players have a height, maybe check the incoming angle
// angle: 0 -> perpendicular, from -90 to 90 // angle: 0 -> perpendicular, from -90 to 90
private bool IsPositionReachableInTime(float futurePosition, float seconds, float radius, float angle) { private bool IsPositionReachableInTime(float futurePosition, float seconds, float radius, float angle) {
float requiredDistance = Mathf.Abs(futurePosition - X) - Width / 2 - radius; var requiredDistance = Mathf.Abs(futurePosition - X) - Width / 2 - radius;
if (requiredDistance < 0) if (requiredDistance < 0)
return true; return true;
if (Mathf.Abs(futurePosition) > Border) if (Mathf.Abs(futurePosition) > Border)
@ -77,33 +105,33 @@ namespace Game {
private float Simulate(Vector2 origin, Vector2 velocity, float radius, float secondsLeft, float secondsUsed, out bool ignore) { private float Simulate(Vector2 origin, Vector2 velocity, float radius, float secondsLeft, float secondsUsed, out bool ignore) {
ignore = false; ignore = false;
Vector2 r = new Vector2(radius, radius); var r = new Vector2(radius, radius);
Vector2 validAreaOrigin = -Dimensions.Singleton.PlaySizeBoards / 2 + r; var validAreaOrigin = -Dimensions.Singleton.PlaySizeBoards / 2 + r;
Vector2 validAreaSize = Dimensions.Singleton.PlaySizeBoards - r * 2; var validAreaSize = Dimensions.Singleton.PlaySizeBoards - r * 2;
Rect area = new Rect(validAreaOrigin, validAreaSize); var area = new Rect(validAreaOrigin, validAreaSize);
// Try to follow this line from origin -> end // Try to follow this line from origin -> end
Vector2 end = origin + velocity * secondsLeft; var end = origin + velocity * secondsLeft;
// Line ends in playground // Line ends in playground
if (area.Contains(end)) if (area.Contains(end))
return end.x; return end.x;
float playerY = Side == Side.Bottom ? area.yMin : area.yMax; var playerY = Side == Side.Bottom ? area.yMin : area.yMax;
Vector2 playerLeft = new Vector2(area.xMin, playerY); var playerLeft = new Vector2(area.xMin, playerY);
Vector2 playerRight = new Vector2(area.xMax, playerY); var playerRight = new Vector2(area.xMax, playerY);
// Horizontal line (player line) -> stop simulating // Horizontal line (player line) -> stop simulating
if (Intersect(origin, end, playerLeft, playerRight, out Vector2 point, out Vector2 rs)) { if (Intersect(origin, end, playerLeft, playerRight, out var point, out var rs)) {
secondsUsed += secondsLeft * rs.x; secondsUsed += secondsLeft * rs.x;
if (!IsPositionReachableInTime(point.x, secondsUsed, radius, Vector2.Angle(velocity, transform.up))) if (!IsPositionReachableInTime(point.x, secondsUsed, radius, Vector2.Angle(velocity, transform.up)))
ignore = true; ignore = true;
return point.x; return point.x;
} }
bool borderHit = false; var borderHit = false;
Vector2 borderHitPoint = Vector2.zero; var borderHitPoint = Vector2.zero;
Vector2 borderRs = Vector2.zero; var borderRs = Vector2.zero;
// Left vertical border // Left vertical border
if (Intersect(origin, end, new Vector2(area.xMin, area.yMin), new Vector2(area.xMin, area.yMax), out point, out rs)) { if (Intersect(origin, end, new Vector2(area.xMin, area.yMin), new Vector2(area.xMin, area.yMax), out point, out rs)) {
@ -121,7 +149,7 @@ namespace Game {
// Any border -> invert x velocity and simulate again from there // Any border -> invert x velocity and simulate again from there
if (borderHit) { if (borderHit) {
float secondsUsedHere = borderRs.x * secondsLeft; var secondsUsedHere = borderRs.x * secondsLeft;
secondsLeft -= secondsUsedHere; secondsLeft -= secondsUsedHere;
secondsUsed += secondsUsedHere; secondsUsed += secondsUsedHere;
velocity = new Vector2(-velocity.x, velocity.y); velocity = new Vector2(-velocity.x, velocity.y);
@ -134,24 +162,22 @@ namespace Game {
return 0; return 0;
} }
private float DistortAmount => 1 - 1 / ((float) Difficulty + 1);
private float Distort(float target) { private float Distort(float target) {
float max = Width / 3; var max = Width / 3;
float distortionOffset = DistortAmount * max; var distortionOffset = DistortAmount * max;
distortionOffset *= target > X ? -1 : 1; distortionOffset *= target > X ? -1 : 1;
return target + distortionOffset; return target + distortionOffset;
} }
private float GetTargetPosition() { private float GetTargetPosition() {
var approaching = GameManager.Singleton.Balls.Where(BallApproaches).ToList(); var approaching = GameManager.Singleton.Balls.Where(BallApproaches).ToList();
while (approaching.Count > 0) { while (approaching.Count > 0) {
// Nearest by Y-Distance // Nearest by Y-Distance
Ball ball = approaching.Aggregate(approaching[0], (prev, current) => YDistanceToBall(current) < YDistanceToBall(prev) ? current : prev); var ball = approaching.Aggregate(approaching[0], (prev, current) => YDistanceToBall(current) < YDistanceToBall(prev) ? current : prev);
float target = Simulate(ball.Rb.position, ball.Rb.velocity, ball.Radius, FutureSeconds, 0, out bool ignore); var target = Simulate(ball.Rb.position, ball.Rb.velocity, ball.Radius, FutureSeconds, 0, out var ignore);
if (!ignore) if (!ignore)
return Distort(target); return Distort(target);
@ -162,35 +188,10 @@ namespace Game {
return IdlePosition; return IdlePosition;
} }
private float currentSmoothV;
private const float SmoothTime = 0.2f;
private void ApproachPosition(float pos) { private void ApproachPosition(float pos) {
float result = Mathf.SmoothDamp(X, pos, ref currentSmoothV, SmoothTime, Speed); var result = Mathf.SmoothDamp(X, pos, ref currentSmoothV, SmoothTime, Speed);
transform.position = new Vector2(result, Y); transform.position = new Vector2(result, Y);
ClampInsideBorders(); ClampInsideBorders();
} }
private bool isApproaching;
private float lastDirection;
private void FixedUpdate() {
float dt = Time.fixedDeltaTime;
float target = GetTargetPosition();
float h = Mathf.Max(Speed * dt, Width / 2);
goingLeft = target < X - h;
goingRight = target > X + h;
if (goingLeft || goingRight) {
isApproaching = false;
lastDirection = goingLeft ? -1 : 1;
TryLinearMove(dt);
}
else {
if (!isApproaching) {
isApproaching = true;
currentSmoothV = Speed * lastDirection;
}
ApproachPosition(target);
}
}
} }
} }

@ -1,26 +1,29 @@
using System;
using System.Collections;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
using Random = UnityEngine.Random;
namespace Game { namespace Game {
public class Ball : NetworkBehaviour { public class Ball : NetworkBehaviour {
private const float SpeedGain = 1.025f;
public Rigidbody2D Rb { get; private set; } public Rigidbody2D Rb { get; private set; }
public Player LastContactPlayer { get; private set; } public Player LastContactPlayer { get; private set; }
public bool IsPermanent { get; set; } public bool IsPermanent { get; set; }
private CircleCollider2D Collider { get; set; } private CircleCollider2D Collider { get; set; }
private const float SpeedGain = 1.025f;
public float Radius { public float Radius {
get => transform.localScale.x * Collider.radius; get => transform.localScale.x * Collider.radius;
set => transform.localScale = new Vector3(1, 1, 1) * value * 2; set => transform.localScale = new Vector3(1, 1, 1) * value * 2;
} }
private void Awake() {
Rb = GetComponent<Rigidbody2D>();
Collider = GetComponent<CircleCollider2D>();
}
private void OnCollisionEnter2D(Collision2D other) { private void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.TryGetComponent(out Player player)) if (other.gameObject.TryGetComponent(out Player player))
PlayerContact(player); PlayerContact(player);
@ -30,11 +33,5 @@ namespace Game {
LastContactPlayer = player; LastContactPlayer = player;
Rb.velocity *= SpeedGain; Rb.velocity *= SpeedGain;
} }
private void Awake() {
Rb = GetComponent<Rigidbody2D>();
Collider = GetComponent<CircleCollider2D>();
}
} }
} }

@ -1,11 +1,10 @@
using System;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
namespace Game { namespace Game {
public abstract class Collectable : NetworkBehaviour { public abstract class Collectable : NetworkBehaviour {
private void OnTriggerEnter2D(Collider2D other) { private void OnTriggerEnter2D(Collider2D other) {
Player player = other.GetComponent<Ball>().LastContactPlayer; var player = other.GetComponent<Ball>().LastContactPlayer;
if (player != null) { if (player != null) {
Destroy(gameObject); Destroy(gameObject);
OnCollect(player); OnCollect(player);

@ -1,4 +1,3 @@
using System;
using UnityEngine; using UnityEngine;
namespace Game { namespace Game {
@ -9,8 +8,8 @@ namespace Game {
private Player thisPlayer, otherPlayer; private Player thisPlayer, otherPlayer;
private void Start() { private void Start() {
Player p1 = GameManager.Singleton.Player1; var p1 = GameManager.Singleton.Player1;
Player p2 = GameManager.Singleton.Player2; var p2 = GameManager.Singleton.Player2;
if (p1.Side == side) { if (p1.Side == side) {
thisPlayer = p1; thisPlayer = p1;
otherPlayer = p2; otherPlayer = p2;
@ -21,16 +20,15 @@ namespace Game {
} }
private void OnTriggerEnter2D(Collider2D ballCollider2D) { private void OnTriggerEnter2D(Collider2D ballCollider2D) {
Ball ball = ballCollider2D.GetComponent<Ball>(); var ball = ballCollider2D.GetComponent<Ball>();
if (ball.LastContactPlayer != null) if (ball.LastContactPlayer != null)
otherPlayer.GainScore(); otherPlayer.GainScore();
GameManager.Singleton.RemoveBall(ball); GameManager.Singleton.RemoveBall(ball);
if (ball.IsPermanent) if (ball.IsPermanent)
GameManager.Singleton.SpawnBall(thisPlayer, true); GameManager.Singleton.SpawnBall(thisPlayer, true);
} }
} }
} }

@ -1,44 +1,56 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Unity.Netcode; using Unity.Netcode;
using Unity.VisualScripting; using Unity.VisualScripting;
using UnityEngine; using UnityEngine;
using UnityEngine.Assertions;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
namespace Game { namespace Game {
public class GameManager : NetworkBehaviour { public class GameManager : NetworkBehaviour {
public static GameManager Singleton { get; private set; }
private void OnEnable() {
Singleton = this;
// Move this to settings
Application.targetFrameRate = 144;
QualitySettings.vSyncCount = 0;
}
public Object ballPrefab; public Object ballPrefab;
public Object playerPrefab; public Object playerPrefab;
public Object modificationPrefab; public Object modificationPrefab;
public ModificationProperties[] modifications; public ModificationProperties[] modifications;
public Object wormholePrefab; public Object wormholePrefab;
public static GameManager Singleton { get; private set; }
public List<Ball> Balls { get; } = new(); public List<Ball> Balls { get; } = new();
public Player Player1 { get; private set; } public Player Player1 { get; private set; }
public Player Player2 { get; private set; } public Player Player2 { get; private set; }
private void Awake() {
SetupPlayers();
SpawnBall(Player1, true);
}
private void OnEnable() {
Singleton = this;
// Move this to settings
Application.targetFrameRate = 144;
QualitySettings.vSyncCount = 0;
}
private IEnumerator Start() {
while (Application.isPlaying) {
yield return new WaitForSeconds(1);
SpawnModification();
}
}
private void SetupPlayers() { private void SetupPlayers() {
Settings.Type = Type.Hybrid; Settings.Type = Type.Hybrid;
Settings.AIDifficulty = Difficulty.Hard; Settings.AIDifficulty = Difficulty.Hard;
var p1Obj = Instantiate(playerPrefab); var p1Obj = Instantiate(playerPrefab);
var p2Obj = Instantiate(playerPrefab); var p2Obj = Instantiate(playerPrefab);
Player p1, p2; Player p1, p2;
switch (Settings.Type) { switch (Settings.Type) {
case Type.AI: case Type.AI:
@ -67,6 +79,7 @@ namespace Game {
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
p1.Side = Side.Bottom; p1.Side = Side.Bottom;
p2.Side = Side.Top; p2.Side = Side.Top;
@ -74,38 +87,31 @@ namespace Game {
Player2 = p2; Player2 = p2;
} }
private void Awake() { private void SpawnWormhole() { }
SetupPlayers();
SpawnBall(Player1, true);
}
private void SpawnWormhole() {
}
private void SpawnModification() { private void SpawnModification() {
Vector2 pos = new Vector2(Random.Range(0, Dimensions.Singleton.PlaySize.x) - Dimensions.Singleton.PlaySize.x / 2, Random.Range(-2, 2)); var pos = new Vector2(Random.Range(0, Dimensions.Singleton.PlaySize.x) - Dimensions.Singleton.PlaySize.x / 2, Random.Range(-2, 2));
ModificationProperties properties = modifications[Random.Range(0, modifications.Length)]; var properties = modifications[Random.Range(0, modifications.Length)];
var mod = Instantiate(modificationPrefab, pos, Quaternion.identity).GetComponent<Modification>(); var mod = Instantiate(modificationPrefab, pos, Quaternion.identity).GetComponent<Modification>();
mod.Properties = properties; mod.Properties = properties;
} }
public void SpawnBall(Player towards, bool isPermanent) { public void SpawnBall(Player towards, bool isPermanent) {
const float startSpeed = 15; const float startSpeed = 15;
Vector2 position = new Vector2(0, -towards.transform.position.y * 0.5f); var position = new Vector2(0, -towards.transform.position.y * 0.5f);
var ball = Instantiate(ballPrefab, position, Quaternion.identity).GetComponent<Ball>(); var ball = Instantiate(ballPrefab, position, Quaternion.identity).GetComponent<Ball>();
ball.Rb.velocity = RandomDirectionTowards(towards) * startSpeed; ball.Rb.velocity = RandomDirectionTowards(towards) * startSpeed;
ball.IsPermanent = isPermanent; ball.IsPermanent = isPermanent;
ball.Radius = 0.5f; ball.Radius = 0.5f;
Balls.Add(ball); Balls.Add(ball);
} }
private static Vector2 RandomDirectionTowards(Player player) { private static Vector2 RandomDirectionTowards(Player player) {
const float maxAngle = 45; const float maxAngle = 45;
float radians = Random.Range(-maxAngle, maxAngle) * Mathf.PI / 180; var radians = Random.Range(-maxAngle, maxAngle) * Mathf.PI / 180;
float x = Mathf.Sin(radians); var x = Mathf.Sin(radians);
float y = Mathf.Cos(radians) * player.Side switch { var y = Mathf.Cos(radians) * player.Side switch {
Side.Top => 1, Side.Top => 1,
Side.Bottom => -1, Side.Bottom => -1,
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
@ -117,6 +123,5 @@ namespace Game {
Balls.Remove(ball); Balls.Remove(ball);
Destroy(ball.gameObject); Destroy(ball.gameObject);
} }
} }
} }

@ -1,9 +1,5 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements;
namespace Game { namespace Game {

@ -1,11 +1,10 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace Game { namespace Game {
public enum ModType { Nerf, Buff } public enum ModType { Nerf, Buff }
public enum ModEffect { Speed, Border } public enum ModEffect { Speed, Border, Width }
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/ModificationProperties", order = 0)] [CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/ModificationProperties", order = 0)]
public class ModificationProperties : ScriptableObject { public class ModificationProperties : ScriptableObject {
public ModType type; public ModType type;
@ -15,5 +14,3 @@ namespace Game {
public Sprite image; public Sprite image;
} }
} }

@ -4,64 +4,90 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements; using UnityEngine.UIElements;
namespace Game { namespace Game {
public enum Side {Top, Bottom}
public class Player : NetworkBehaviour {
public Side Side { get; set; } public enum Side { Top, Bottom }
public class Player : NetworkBehaviour {
private const float BaseSpeed = 15;
private const float SpeedMultiplier = 1.5f;
// Unit distance from zero
private readonly float baseBorder = Dimensions.Singleton.PlaySize.x / 2 * 0.85f;
private readonly float borderSummand = Dimensions.Singleton.PlaySize.x / 2 * 0.15f;
private const float BaseWidth = 6;
private const float WidthMultiplier = 1.5f;
protected bool goingLeft, goingRight; protected bool goingLeft, goingRight;
public Side Side { get; set; }
private VisualElement Panel { get; set; } private VisualElement Panel { get; set; }
private List<ModificationProperties> Modifications { get; } = new(); private List<ModificationProperties> Modifications { get; } = new();
private int Score { get; set; } private int Score { get; set; }
// Units per second private float GetModified(ModEffect effect, float baseValue, float modValue, Func<float, float, float> combineFunc, Func<float, float> invertFunc) {
private const float BaseSpeed = 15; return Modifications.Where(m => m.effect == effect)
private const float SpeedMultiplier = 1.5f; .Aggregate(baseValue,
(current, mod) => combineFunc(current, mod.type switch {
ModType.Buff => modValue,
ModType.Nerf => invertFunc(modValue),
_ => throw new ArgumentOutOfRangeException()
})
);
}
protected float Speed { protected float Speed {
get { get {
return Modifications.Where(m => m.effect == ModEffect.Speed) float value = GetModified(ModEffect.Speed, BaseSpeed, SpeedMultiplier, (f, f1) => f * f1, f => 1 / f);
.Aggregate(BaseSpeed, (current, speedMod) => current * speedMod.type switch { return value;
ModType.Buff => SpeedMultiplier,
ModType.Nerf => 1 / SpeedMultiplier,
_ => throw new ArgumentOutOfRangeException()
});
} }
} }
// Unit distance from zero
private readonly float baseBorder = Dimensions.Singleton.PlaySize.x / 2 * 0.85f;
private readonly float borderSummand = Dimensions.Singleton.PlaySize.x / 2 * 0.15f;
protected float Border { protected float Border {
get { get {
return Mathf.Min(Modifications.Where(m => m.effect == ModEffect.Border) float value = GetModified(ModEffect.Border, baseBorder, borderSummand, (f, f1) => f + f1, f => -f);
.Aggregate(baseBorder, (current, borderMod) => current + borderMod.type switch { return Mathf.Clamp(value, Width / 2, Dimensions.Singleton.PlaySize.x / 2);
ModType.Buff => borderSummand,
ModType.Nerf => -borderSummand,
_ => throw new ArgumentOutOfRangeException()
}),
Dimensions.Singleton.PlaySize.x / 2
);
} }
} }
protected float Width {
get {
float value = GetModified(ModEffect.Width, BaseWidth, WidthMultiplier, (f, f1) => f * f1, f => 1 / f);
return Mathf.Clamp(value, 0.1f, Dimensions.Singleton.PlaySize.x);
}
}
protected float Width => transform.localScale.x;
private float LeftEdge => X - Width / 2; private float LeftEdge => X - Width / 2;
private float RightEdge => X + Width / 2; private float RightEdge => X + Width / 2;
protected float X => transform.position.x; protected float X => transform.position.x;
protected float Y => transform.position.y; protected float Y => transform.position.y;
private void Update() {
transform.localScale = new Vector3(Width, 1, 1);
}
protected void Start() {
var y = Side switch {
Side.Bottom => -Dimensions.Singleton.PlaySizeBoards.y / 2,
Side.Top => Dimensions.Singleton.PlaySizeBoards.y / 2,
_ => throw new ArgumentOutOfRangeException()
};
transform.position = new Vector2(0, y);
if (Side == Side.Top)
transform.Rotate(transform.forward, 180);
Panel = GameUI.Singleton.PlayerPanel(Side);
UpdatePanel();
}
public void GainScore() { public void GainScore() {
Score++; Score++;
UpdatePanel(); UpdatePanel();
@ -73,10 +99,10 @@ namespace Game {
public IEnumerator ProcessModification(ModificationProperties properties) { public IEnumerator ProcessModification(ModificationProperties properties) {
Modifications.Add(properties); Modifications.Add(properties);
VisualElement element = GameUI.AddModification(Panel, properties); var element = GameUI.AddModification(Panel, properties);
float doNothingTime = properties.activeDuration * 0.7f; var doNothingTime = properties.activeDuration * 0.7f;
float animateTime = properties.activeDuration * 0.3f; var animateTime = properties.activeDuration * 0.3f;
yield return new WaitForSeconds(doNothingTime); yield return new WaitForSeconds(doNothingTime);
@ -90,33 +116,20 @@ namespace Game {
element.RemoveFromHierarchy(); element.RemoveFromHierarchy();
Modifications.Remove(properties); Modifications.Remove(properties);
} }
protected void ClampInsideBorders() { protected void ClampInsideBorders() {
if (LeftEdge < -Border) if (LeftEdge < -Border)
transform.Translate(Vector2.right * (-Border - LeftEdge), Space.World); transform.Translate(Vector2.right * (-Border - LeftEdge), Space.World);
if (RightEdge > Border) if (RightEdge > Border)
transform.Translate(Vector2.left * (RightEdge - Border), Space.World); transform.Translate(Vector2.left * (RightEdge - Border), Space.World);
} }
protected void TryLinearMove(float h) { protected void TryLinearMove(float h) {
Vector2 trans = new Vector2((goingLeft ? -1 : 0) + (goingRight ? 1 : 0), 0); var trans = new Vector2((goingLeft ? -1 : 0) + (goingRight ? 1 : 0), 0);
trans *= Speed * h; trans *= Speed * h;
transform.Translate(trans, Space.World); transform.Translate(trans, Space.World);
ClampInsideBorders(); ClampInsideBorders();
} }
protected void Start() {
float y = Side switch {
Side.Bottom => -Dimensions.Singleton.PlaySizeBoards.y / 2,
Side.Top => Dimensions.Singleton.PlaySizeBoards.y / 2,
_ => throw new ArgumentOutOfRangeException()
};
transform.position = new Vector2(0, y);
if (Side == Side.Top)
transform.Rotate(transform.forward, 180);
Panel = GameUI.Singleton.PlayerPanel(Side);
UpdatePanel();
}
} }
} }

@ -24,7 +24,7 @@ namespace Game {
goingRight = false; goingRight = false;
}; };
} }
private void FixedUpdate() { private void FixedUpdate() {
if (!isThisClient) if (!isThisClient)
return; return;
@ -33,13 +33,13 @@ namespace Game {
var keyboard = Keyboard.current; var keyboard = Keyboard.current;
switch (Side) { switch (Side) {
case Side.Top: case Side.Top:
goingLeft = keyboard.aKey.isPressed;
goingRight = keyboard.dKey.isPressed;
break;
case Side.Bottom:
goingLeft = keyboard.leftArrowKey.isPressed; goingLeft = keyboard.leftArrowKey.isPressed;
goingRight = keyboard.rightArrowKey.isPressed; goingRight = keyboard.rightArrowKey.isPressed;
break; break;
case Side.Bottom:
goingLeft = keyboard.aKey.isPressed;
goingRight = keyboard.dKey.isPressed;
break;
} }
} }

@ -1,10 +1,10 @@
namespace Game { namespace Game {
public enum Type { RealOnline, RealOffline, Hybrid, AI } public enum Type { RealOnline, RealOffline, Hybrid, AI }
public enum Difficulty { VeryEasy, Easy, Medium, Hard, VeryHard } public enum Difficulty { VeryEasy, Easy, Medium, Hard, VeryHard }
public struct Settings { public struct Settings {
public static Type Type; public static Type Type;
public static Difficulty AIDifficulty; public static Difficulty AIDifficulty;
}; }
} }

@ -1,21 +1,17 @@
using System;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
namespace Game { namespace Game {
public class Wormhole : NetworkBehaviour { public class Wormhole : NetworkBehaviour {
public float Size { get; set; } public float Size { get; set; }
private void Start() { private void Start() {
transform.localScale = new Vector3(Size, Size, Size); transform.localScale = new Vector3(Size, Size, Size);
} }
private void FixedUpdate() { private void FixedUpdate() {
foreach (var ball in GameManager.Singleton.Balls) { foreach (var ball in GameManager.Singleton.Balls) { }
}
} }
} }
} }

@ -7,15 +7,21 @@ using UnityEngine.UIElements;
public class GameUI : MonoBehaviour { public class GameUI : MonoBehaviour {
private UIDocument document; public delegate void ButtonDown(Side verticalSide, string horizontalSide);
public delegate void ButtonUp(Side side, string direction);
public static GameUI Singleton; public static GameUI Singleton;
public ButtonDown buttonDown;
public ButtonUp buttonUp;
private UIDocument document;
private List<VisualElement> PlayerPanels => document.rootVisualElement.Children().Where(e => e.ClassListContains("player_panel")).ToList();
private void Awake() { private void Awake() {
Singleton = this; Singleton = this;
document = GetComponent<UIDocument>(); document = GetComponent<UIDocument>();
PreparePlayerPanels(); PreparePlayerPanels();
} }
private List<VisualElement> PlayerPanels => document.rootVisualElement.Children().Where(e => e.ClassListContains("player_panel")).ToList();
public VisualElement PlayerPanel(Side side) { public VisualElement PlayerPanel(Side side) {
return side switch { return side switch {
@ -24,10 +30,10 @@ public class GameUI : MonoBehaviour {
_ => throw new ArgumentOutOfRangeException(nameof(side), side, null) _ => throw new ArgumentOutOfRangeException(nameof(side), side, null)
}; };
} }
public static VisualElement AddModification(VisualElement playerPanel, ModificationProperties properties) { public static VisualElement AddModification(VisualElement playerPanel, ModificationProperties properties) {
VisualElement listElement = ModsList(playerPanel, properties.type); var listElement = ModsList(playerPanel, properties.type);
VisualElement newElement = new() { VisualElement newElement = new() {
style = { style = {
backgroundImage = Background.FromSprite(properties.image) backgroundImage = Background.FromSprite(properties.image)
@ -47,24 +53,19 @@ public class GameUI : MonoBehaviour {
} }
private void PreparePlayerPanels() { private void PreparePlayerPanels() {
float pixelHeight = Dimensions.Singleton.PanelHeightPixels; var pixelHeight = Dimensions.Singleton.PanelHeightPixels;
PlayerPanel(Side.Top).style.height = PlayerPanel(Side.Bottom).style.height = new Length(pixelHeight, LengthUnit.Pixel); PlayerPanel(Side.Top).style.height = PlayerPanel(Side.Bottom).style.height = new Length(pixelHeight, LengthUnit.Pixel);
SetupGoButton(Side.Top, "left"); SetupGoButton(Side.Top, "left");
SetupGoButton(Side.Top, "right"); SetupGoButton(Side.Top, "right");
SetupGoButton(Side.Bottom, "left"); SetupGoButton(Side.Bottom, "left");
SetupGoButton(Side.Bottom, "right"); SetupGoButton(Side.Bottom, "right");
} }
public delegate void ButtonDown(Side verticalSide, string horizontalSide);
public delegate void ButtonUp(Side side, string direction);
public ButtonDown buttonDown;
public ButtonUp buttonUp;
private Label GetGoButton(Side sideVertical, string sideHorizontal) { private Label GetGoButton(Side sideVertical, string sideHorizontal) {
return PlayerPanel(sideVertical).Q<Label>("go_" + sideHorizontal); return PlayerPanel(sideVertical).Q<Label>("go_" + sideHorizontal);
} }
private void SetupGoButton(Side sideVertical, string sideHorizontal) { private void SetupGoButton(Side sideVertical, string sideHorizontal) {
var element = GetGoButton(sideVertical, sideHorizontal); var element = GetGoButton(sideVertical, sideHorizontal);
element.RegisterCallback<PointerDownEvent>(_ => buttonDown(sideVertical, sideHorizontal)); element.RegisterCallback<PointerDownEvent>(_ => buttonDown(sideVertical, sideHorizontal));

@ -5,26 +5,26 @@ using UnityEngine.UIElements;
public class MenuUI : MonoBehaviour { public class MenuUI : MonoBehaviour {
private VisualElement mainMenu, playMenu, settingsMenu;
private VisualElement root; private VisualElement root;
private VisualElement mainMenu, playMenu, settingsMenu;
private void OnEnable() { private void OnEnable() {
root = GetComponent<UIDocument>().rootVisualElement; root = GetComponent<UIDocument>().rootVisualElement;
mainMenu = root.Q("main_menu"); mainMenu = root.Q("main_menu");
playMenu = root.Q("play_menu"); playMenu = root.Q("play_menu");
settingsMenu = root.Q("settings_menu"); settingsMenu = root.Q("settings_menu");
mainMenu.Q("btn_play").RegisterCallback<ClickEvent>(PlayPressed); mainMenu.Q("btn_play").RegisterCallback<ClickEvent>(PlayPressed);
mainMenu.Q("btn_settings").RegisterCallback<ClickEvent>(SettingsPressed); mainMenu.Q("btn_settings").RegisterCallback<ClickEvent>(SettingsPressed);
playMenu.Q("btn_find").RegisterCallback<ClickEvent>(FindPressed); playMenu.Q("btn_find").RegisterCallback<ClickEvent>(FindPressed);
playMenu.Q("btn_host").RegisterCallback<ClickEvent>(HostPressed); playMenu.Q("btn_host").RegisterCallback<ClickEvent>(HostPressed);
playMenu.Q("btn_join").RegisterCallback<ClickEvent>(JoinPressed); playMenu.Q("btn_join").RegisterCallback<ClickEvent>(JoinPressed);
playMenu.Q("btn_bot").RegisterCallback<ClickEvent>(BotPressed); playMenu.Q("btn_bot").RegisterCallback<ClickEvent>(BotPressed);
playMenu.Q("btn_bots").RegisterCallback<ClickEvent>(BotsPressed); playMenu.Q("btn_bots").RegisterCallback<ClickEvent>(BotsPressed);
// PlayPressed(null); // PlayPressed(null);
// BotPressed(null); // BotPressed(null);
} }
@ -39,17 +39,11 @@ public class MenuUI : MonoBehaviour {
settingsMenu.style.display = DisplayStyle.Flex; settingsMenu.style.display = DisplayStyle.Flex;
} }
private void FindPressed(ClickEvent evt) { private void FindPressed(ClickEvent evt) { }
}
private void HostPressed(ClickEvent evt) { private void HostPressed(ClickEvent evt) { }
}
private void JoinPressed(ClickEvent evt) { private void JoinPressed(ClickEvent evt) { }
}
private void BotPressed(ClickEvent evt) { private void BotPressed(ClickEvent evt) {
Settings.Type = Type.Hybrid; Settings.Type = Type.Hybrid;

@ -9,9 +9,8 @@ public class NetworkCommandLine : MonoBehaviour {
private void Start() { private void Start() {
_netManager = GetComponentInParent<NetworkManager>(); _netManager = GetComponentInParent<NetworkManager>();
if (Application.isEditor) { if (Application.isEditor)
return; return;
}
var args = GetCommandlineArgs(); var args = GetCommandlineArgs();

Loading…
Cancel
Save