Unity Project - Arkanoid24, Ball System
๐Ball System
- ๊ธฐ์กด ๊ณต ์์คํ
์
Ball.cs
์์ ์ถฉ๋, ์ด๋, ์ค์ ๋ฑ ๋ชจ๋ ๊ฒ์ด ์ด๋ฃจ์ด์ก๋ค. - ์ด๋ ๋์ค์ ๊ณต ๊ด๋ จ ์ฌํญ์ ์์ ํ ๋ ์์ฃผ ๋ถํธํ ์ ์๋ค๊ณ ์๊ฐํด ๋ชจ๋ํ๋ฅผ ์งํํ๋ค.
Ball Modularzation
ScriptName | Description |
---|---|
BallPreference.cs | Ball.cs Base - ๋ณผ ์ค์ ๊ด๋ จ (์ด๋์๋, ์์ ์ ๋ฑ) |
Ball.cs | Preference๋ฅผ ์์ ๋ฐ์ ์ค์ง์ ์ด ๋ณผ ์ํ(Ball State) ์ด๋ ๋ก์ง ๋ฐ ์ค๋น ๋ก์ง |
BallCollision.cs | ๋ณผ์ ์ถฉ๋ ๋ถ๋ถ (๋ฒฝ์ด๋ ํ๋ ์ด์ด ๋๋ ์ธ๋ถ ๊ฒฝ๊ณ์ ) |
BallPreference.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
using UnityEngine; public class BallPreference : MonoBehaviour { public enum BALL_STATE { READY, LAUNCH } #region Member Variables [Header("Ball Information")] [Range(4f, 6f)] public float MinSpeed = 4f; [Range(14f, 16f)] public float MaxSpeed = 16f; // Ball Start Default Speed [Range(7f, 12f)] public float defaultSpeed = 7f; [SerializeField] protected float _currentSpeed; protected Rigidbody2D _paddleRbody; protected Rigidbody2D _ballRbody; // Game Pause protected Vector2 _prevVelocity; protected bool _isStarted = true; public float ballIncreaseSpeedScope = 0.5f; // Player Depend, Reference protected GameObject _playerObject; public BALL_STATE BallState { get; set; } = BALL_STATE.READY; // Getter public GameObject BallOwner { get { return _playerObject; } } public BallSkillState BallSkill; #endregion #region Unity Flow protected virtual void Awake() { _ballRbody = GetComponent<Rigidbody2D>(); } protected virtual void Start() { _currentSpeed = defaultSpeed; BallSkill = BallOwner.GetComponent<BallSkillState>(); } protected virtual void FixedUpdate() { if(Managers.Game.State != GameState.Play) { if (_isStarted) { _isStarted = false; _prevVelocity = _ballRbody.velocity; } _ballRbody.velocity = Vector2.zero; return; } if (!_isStarted) { _ballRbody.velocity = _prevVelocity; _isStarted = true; } } #endregion #region Utility public void AssignPlayer(GameObject player) { _playerObject = player; GetPlayerComponent(); } private void GetPlayerComponent() { _paddleRbody = _playerObject.GetComponent<Rigidbody2D>(); } public void SetAdditionalCurrentSpeed(float additionalSpeed) { _currentSpeed = defaultSpeed + additionalSpeed; if (_currentSpeed > MaxSpeed) _currentSpeed = MaxSpeed; else if(_currentSpeed < MinSpeed) _currentSpeed = MinSpeed; } public void BallHitBrick() { BallSkill.SetSpeed(BallOwner); } #endregion }
Ball.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
using System; using UnityEngine; public class Ball : BallPreference { #region Member Variables public bool isCatchLaunch = false; public int _defaultPower = 1; public int _maxPower = 1; public float _posX; // Event private event Action OnBallLaunch; #endregion #region Unity Flow protected override void Awake() { // Get Component base.Awake(); } protected override void Start() { base.Start(); OnBallLaunch += BallToStart; SetAdditionalCurrentSpeed(BallOwner.GetComponent<BallSkillState>().BallExtraSpeed); SetPower(BallSkill.BallExtraPower); } protected override void FixedUpdate() { base.FixedUpdate(); BallStateUpdateMethod(); } #endregion #region Ball State private void BallStateUpdateMethod() { Action ballAction = BallState switch { BALL_STATE.READY => () => BallToReady(), BALL_STATE.LAUNCH => () => BallToLaunch(), _ => () => { } }; ballAction(); } private void BallToStart() { if (BallState != BALL_STATE.READY) return; if(BallSkill.CurrentSkill == Items.Catch) isCatchLaunch = true; BallState = BALL_STATE.LAUNCH; CalculateBallPosToPaddle(); } private void CalculateBallPosToPaddle() { var paddleWidth = _playerObject.GetComponent<BoxCollider2D>().bounds.size.x; var posX = _ballRbody.position.x - _paddleRbody.position.x; posX = posX / paddleWidth; var direction = new Vector2(posX, 1).normalized; if (posX != 0) _ballRbody.velocity = direction * _currentSpeed; else _ballRbody.velocity = Vector2.up * defaultSpeed; isCatchLaunch = false; } private void BallToReady() { FollowThePaddle(_posX); } private void FollowThePaddle(float posX = 0f) { Vector2 paddlePos = _paddleRbody.position; Vector2 newBallPos = new Vector2(paddlePos.x, paddlePos.y + 0.5f); _ballRbody.position = newBallPos + new Vector2(posX, 0f); _ballRbody.velocity = Vector2.zero; } private void BallToLaunch() { var ballVelocitySpeed = _ballRbody.velocity.magnitude; if(ballVelocitySpeed > _currentSpeed || ballVelocitySpeed < _currentSpeed) { _ballRbody.velocity = _ballRbody.velocity.normalized * _currentSpeed; } } #endregion #region Item SKill public void CheckCatchActivation() { if (BallSkill.CurrentSkill == Items.Catch) { BallState = BALL_STATE.READY; _posX = transform.position.x - _paddleRbody.transform.position.x; } } public void SetPower(int extraPower) { _maxPower = _defaultPower + extraPower; if(BallSkill.CurrentSkill == Items.Power) { transform.localScale = transform.localScale * 2f; } } #endregion public void CallBallLaunch() { OnBallLaunch?.Invoke(); } private void OnDestroy() { OnBallLaunch -= BallToStart; } }
Ball.cs
๋BallPreference
๋ฅผ ์์ ๋ฐ์์ ๋ณผ์ ์ค์ ์ ๋ค๊ณ ์ ๋ก์ง๋ง ์งํํ๊ฒ ๋ณ๊ฒฝํ๋ค.- ๊ธฐ๋ณธ์ ์ธ ๋ณผ ์ํ(State)์ ์ค์ (Speed, Owner)๋ฑ์ ์ ๋ถ Preference์์ ์ฒ๋ฆฌํ๋ค.
- ์ด๋ ๊ฒ ํจ์ผ๋ก์จ
Ball.cs
๋ ์คํฌ๊ณผ ์ค์ง์ ์ธ ๋์(velocity) ๋ถ๋ถ๋ง ์ดํํ๋ฉด ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using static BallPreference;
public class BallCollision : MonoBehaviour
{
private Ball _ball;
private Rigidbody2D _ballRbody;
private void Awake()
{
_ballRbody = GetComponent<Rigidbody2D>();
_ball = GetComponent<Ball>();
}
#region Collision
private void OnCollisionEnter2D(Collision2D col)
{
var activePlayer = Managers.Player.GetActivePlayers();
foreach (var player in activePlayer)
{
if (col.gameObject == player && _ball.BallState != BALL_STATE.READY)
{
SFX.Instance.PlayOneShot(SFX.Instance.paddleHit);
float paddleWidth = col.collider.bounds.size.x;
_ball.CheckCatchActivation();
if (player.GetComponent<BallSkillState>().CurrentSkill != Items.Catch)
{
var posX = HitFactor(col.transform.position, paddleWidth);
var direction = new Vector2(posX, 1).normalized;
_ballRbody.velocity = _ballRbody.velocity.magnitude * direction;
}
}
else if (col.gameObject.CompareTag("Brick"))
{
_ball.BallHitBrick();
}
}
}
#endregion
#region Utility
private float HitFactor(Vector2 paddlePos, float paddleWidth)
{
return (_ballRbody.position.x - paddlePos.x) / paddleWidth * 2f;
}
#endregion
}
- ๊ณต์ ์ถฉ๋ ๋ถ๋ถ์์๋ ์ค์ ์ถฉ๋์ด ์ผ์ด๋ฌ์ ๋ ์คํํด์ผํ ๋ถ๋ถ๋ง ๋ชจ๋ํ ํด๋จ๋ค.
ํ๋ ์ด์ด (๊ฐ๋๋ฅผ ์ํจ)
์๋ฒฝ๋
๋ถ๋ถ์ผ๋ก ๋๋์ด ์ฒ๋ฆฌํ๋ค.
๐๏ธServiceLocator
- ์๋น์ค ๋ก์ผ์ดํฐ๋ ์์กด์ฑ ์ฃผ์ (Dependency Injection)๋ฅผ ์ค์์์ ์ฒ๋ฆฌํด์ฃผ๋ ์์คํ ์ด๋ค.
- ์๋น์ค๋ฅผ ๋ฑ๋ก ํด๋๊ณ ์ด๋์๋ ์ ๊ทผํ์ฌ ํด๋น ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์๋ ๋งค๋์ ๊ฐ์ ์์คํ ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections.Generic;
using UnityEngine;
public static class ServiceLocator
{
private static readonly Dictionary<System.Type, Object> _services =
new Dictionary<System.Type, Object>();
public static void RegisterService<T>(T service) where T : Object
{
_services[service.GetType()] = service;
}
public static T GetService<T>() where T : Object
{
if (_services.TryGetValue(typeof(T), out Object service))
{
return service as T;
}
return null;
}
}
์ฑ๊ธํค ํจํด๊ณผ ์๋น์ค ๋ก์ผ์ดํฐ
Singleton
- ์ฑ๊ธํค ํจํด์ ํด๋์ค์ ์ธ์คํด์ค๊ฐ ํ๋๋ง ์์์ ๋ณด์ฅํ๋ ํจํด์ด๋ค.
static
์์ฑ์ด๋ method๋ฅผ ํตํด์ ํด๋์ค ์ ์ญ์ ์์ธ์ค ์ง์ ์ ์ ๊ณตํ๋ค.- ์ ์ญ์์ ์์ธ์ค๊ฐ ๊ฐ๋ฅํ์ ์ ๊ฐ๋จํ ์ฌ์ฉ์ ์ ๊ณตํ์ง๋ง
์ปคํ๋ง
์ผ๋ก ์ด๋ฃจ์ด์ง ์ ์๋ค. - ์ ์ญ ์์ธ์ค๋ฅผ ํ ์ ์๋ ์ด์ ์ด ๋จ์ ์ผ๋ก๋ ์์ฉํ๋๋ฐ, ๋จ์ ํ
์คํธ๋ฅผ ์ด๋ ต๊ฒ ๋ง๋ ๋ค.
Service Locator
- ์๋น์ค ๋ก์ผ์ดํฐ๋ ์๋น์ค์ ๊ตฌ์ฑ์์๋ฅผ ๋ฑ๋กํ๊ณ ํ์ํ ๋ ๊ฒ์ํด์ ์ฌ์ฉํ ์ ์๋ค.
- ์ฑ๊ธํค ํจํด๊ณผ ๋งค์ฐ ์ ์ฌํ์ง๋ง ๊ฒฐํฉ๋๊ฐ ์ฑ๊ธํค๊ณผ๋ ๋ค๋ฅด๊ฒ ์ ์ฉ๋๋ค.
- ํด๋์ค๊ฐ ์๋ก์ ๋ํ ์ฐธ์กฐ๋ฅผ ์ง์ ์ธ์คํด์คํํ๊ฑฐ๋ ๋ณด์ ํ์ง ์๊ธฐ ๋๋ฌธ์,
ํด๋์ค๊ฐ์ ๊ฒฐํฉ์ ์ค์ธ๋ค. ํ์ง๋ง ํด๋์ค ์ข ์์ฑ์ ์จ๊ธธ ์ ์๋ค.
์ ๋ฆฌํ๋ฉฐโฆ
๊ณต ๋ชจ๋ํ
- ๋ชจ๋ํ๋ฅผ ์งํํ๋ฉด์ ํฌ๊ฒ ์ด๋ ค์ ๋ ๋ถ๋ถ์ ์กด์ฌํ์ง ์์๋ค.
- ๊ทธ๋ ๊ทธ๋ด ๊ฒ์ด ๊ธฐ์กด ๋ด์ฉ์ ์ธ๋ถํ ์ํค๋ ์์
์ด์๊ณ ์ธ๋ถํ ์ํจ ๋ด์ฉ์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ์๋
์์์ ํํ๊ฐ ๋ง๋ค๊ณ ํ๋จํ์ฌ ์ค์ ๊ฐ์ ๊ทธ๋๋ก ์์๋ฐ๋ ํํ๋ก ์ฌ์ฉ ํ๊ธฐ ๋๋ฌธ์ด๋ค. - ๊ทธ๋๋ ์ค์ , ๋ณผ ์ค์ ์ก์ , ์ถฉ๋ ๋ถ๋ถ์ ๋ฐ๋ก ๋๋ ๊ด๋ฆฌํ๋ ๋ค๋ฅธ ํ์์ด ๋ด ์ฝ๋์ ์ ๊ทผํ ๋๋ ์ฉ์ดํ๋ค.
์๋น์ค ๋ก์ผ์ดํฐ
- ์๋น์ค ๋ก์ผ์ดํฐ ํจํด์ ๊ธฐ์กด ์ฑ๊ธํค ํํ์ ๋งค๋์ ๋ค์ ์ด๋์ ๋ ๋์ฒดํ ์ ์๊ฒ ํด์ฃผ์๋ค.
- ๋งค๋์ ๋ค๋ ์ ์ญ์์ ์์ธ์คํ ์ ์๋ค๋ ์ ์ ์ ์ธํ๋ฉด
SRP(๋จ์ผ ์ฑ ์ ์์น)
์ ์ค์ํด์ผํ๋ค. - ์๋น์ค ๋ก์ผ์ดํฐ๋ฅผ ์ฌ์ฉํด ์ด๋์๋ ์์กด์ฑ์ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ค.
- ์ด๋
GameManager
์ ๊ฐ์ ๋งค๋์ ์์Player
๊ฐ์ ์ ์ญ ์ ๋ณด๋ฅผ ๋ค๋ฃฐ ํ์๊ฐ ์์ด์ก๋ค.
This post is licensed under CC BY 4.0 by the author.