Breakout Clone
This is a Breakout clone made in 5 days with Unity and UniRx. It was a programming task assigned to me as part of the hiring process for a Unity software engineer position at a KidsLoop. For this project I thought it would be fun to combine reactive programming and the model-view-presenter (MVP) pattern. So all key objects (ball, paddle, brick, etc.) are represented by both a MonoBehaviour-derived presenter and a POCO model which the presenter references.



Here’s an example of the MVP + reactive programming style:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using UniRx;
public class Paddle
{
public Paddle()
{
Width = new ReactiveProperty<float>(1);
ResetBallPos = new ReactiveCommand<Unit>();
}
public IReactiveProperty<float> Width { get; }
public ReactiveCommand<Unit> ResetBallPos { get; }
}
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
using UniRx;
using UnityEngine;
public class PaddlePresenter : MonoBehaviour
{
[SerializeField]
private BallPresenter _ballPresenter;
[SerializeField]
private Transform _initialBallPosTrfm;
[SerializeField]
private Transform _graphicTrfm;
public void Init()
{
Paddle = new Paddle();
Observable
.EveryUpdate()
.Select(_ => Input.mousePosition)
.Subscribe(UpdateXPosition)
.AddTo(this);
Paddle
.Width
.Subscribe(xScale => _graphicTrfm.localScale = new Vector3(xScale, _graphicTrfm.localScale.y))
.AddTo(this);
Paddle
.ResetBallPos
.Subscribe(_ => ResetBallPos())
.AddTo(this);
}
public Paddle Paddle { get; private set; }
private void UpdateXPosition(Vector3 mousePos)
{
mousePos.x = Mathf.Clamp(mousePos.x, 0, Screen.width);
var xPos = Camera.main.ScreenToWorldPoint(mousePos).x;
transform.position = new Vector3(xPos, transform.position.y, transform.position.z);
}
private void ResetBallPos()
{
_ballPresenter.transform.parent = transform;
_ballPresenter.transform.position = _initialBallPosTrfm.position;
}
}