Skip to content
2 changes: 2 additions & 0 deletions VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ internal class IconLookup : IIconLookup
public Texture2D Lookup<T>(T mb, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) where T : class
{
switch (mb) {
case BallShotComponent _: return Icons.BallRoller(size, color);
case BallComponent _: return Icons.Ball(size, color);
case BallRollerComponent _: return Icons.BallRoller(size, color);
case BumperComponent _: return Icons.Bumper(size, color);
Expand Down Expand Up @@ -330,6 +331,7 @@ public Texture2D Lookup<T>(T mb, IconSize size = IconSize.Large, IconColor color

public void DisableGizmoIcons()
{
Icons.DisableGizmo<BallShotComponent>();
Icons.DisableGizmo<BallComponent>();
Icons.DisableGizmo<BallRollerComponent>();
Icons.DisableGizmo<BumperComponent>();
Expand Down
40 changes: 40 additions & 0 deletions VisualPinball.Unity/VisualPinball.Unity/Common/Handles2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Visual Pinball Engine
// Copyright (C) 2023 freezy and VPE Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

#if UNITY_EDITOR

using UnityEditor;
using UnityEngine;

namespace VisualPinball.Unity
{
public static class Handles2
{
public static void DrawArrow(Vector3 from, Vector3 to, float width = 1f, float arrowHeadLength = 0.025f, float arrowHeadAngle = 20.0f, bool bothSides = false)
{
var direction = to - from;
var right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1);
var left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1);
Handles.DrawAAPolyLine(width, from, to);
Handles.DrawAAPolyLine(width, to + right * arrowHeadLength, to, to + left * arrowHeadLength);
if (bothSides) {
Handles.DrawAAPolyLine(width, from - right * arrowHeadLength, from, from - left * arrowHeadLength);
}
}
}
}

#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions VisualPinball.Unity/VisualPinball.Unity/Game/BallDebugComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.InputSystem;

namespace VisualPinball.Unity
{
public abstract class BallDebugComponent : MonoBehaviour
{
protected PhysicsEngine _physicsEngine;
protected PlayfieldComponent _playfield;
protected Player _player;
protected Matrix4x4 _ltw;
protected Matrix4x4 _wtl;

protected Plane _playfieldPlane;

protected int _ballId = 0;

private void Awake()
{
_physicsEngine = GetComponentInParent<PhysicsEngine>();
_playfield = GetComponentInParent<PlayfieldComponent>();
_player = GetComponentInParent<Player>();

_ltw = Physics.VpxToWorld;
_wtl = Physics.WorldToVpx;

var p1 = _ltw.MultiplyPoint(new Vector3(-100f, 100f, 0));
var p2 = _ltw.MultiplyPoint(new Vector3(100f, 100f, 0));
var p3 = _ltw.MultiplyPoint(new Vector3(100f, -100f, 0));
_playfieldPlane.Set3Points(p1, p2, p3);
}


protected bool GetCursorPositionOnPlayfield(out float3 vpxPos, out float3 worldPos)
{
vpxPos = float3.zero;
worldPos = float3.zero;
if (!Camera.main) {
return false;
}

var mouseOnScreenPos = Mouse.current.position.ReadValue();
var ray = Camera.main.ScreenPointToRay(mouseOnScreenPos);

if (_playfieldPlane.Raycast(ray, out var enter)) {
worldPos = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter));
vpxPos = _wtl.MultiplyPoint(worldPos);

// todo check playfield bounds
return true;
}
return false;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
namespace VisualPinball.Unity
{
[PackAs("BallRoller")]
public class BallRollerComponent : MonoBehaviour, IPackable
public class BallRollerComponent : BallDebugComponent, IPackable
{
#region Packaging

Expand All @@ -35,68 +35,26 @@ public void UnpackReferences(byte[] bytes, Transform root, PackagedRefs refs, Pa

#endregion

private PhysicsEngine _physicsEngine;
private PlayfieldComponent _playfield;
private Matrix4x4 _ltw;
private Matrix4x4 _wtl;

private Plane _playfieldPlane;

private int _ballId = 0;

private void Awake()
{
_playfield = GetComponentInChildren<PlayfieldComponent>();

_ltw = Physics.VpxToWorld;
_wtl = Physics.WorldToVpx;

var p1 = _ltw.MultiplyPoint(new Vector3(-100f, 100f, 0));
var p2 = _ltw.MultiplyPoint(new Vector3(100f, 100f, 0));
var p3 = _ltw.MultiplyPoint(new Vector3(100f, -100f, 0));
_playfieldPlane.Set3Points(p1, p2, p3);
_physicsEngine = GetComponentInChildren<PhysicsEngine>();
}

private void Update()
{
if (Camera.main == null || _playfield == null) {
if (!Camera.main || !_playfield || !_player) {
return;
}

// find nearest ball
if (Mouse.current.middleButton.wasPressedThisFrame) {
if (GetCursorPositionOnPlayfield(out var mousePosition)) {
var nearestDistance = float.PositiveInfinity;
BallState nearestBall = default;
var ballFound = false;

using (var enumerator = _physicsEngine.Balls.GetEnumerator()) {
while (enumerator.MoveNext()) {
var ball = enumerator.Current.Value;

if (ball.IsFrozen) {
continue;
}
var distance = math.distance(mousePosition, ball.Position.xy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestBall = ball;
ballFound = true;
_ballId = ball.Id;
}
}
}
if (GetCursorPositionOnPlayfield(out var mousePosition, out var _)) {

if (ballFound) {
UpdateBall(ref nearestBall, mousePosition);
if (_player.BallManager.FindNearest(mousePosition.xy, out var nearestBall)) {
_ballId = nearestBall.Id;
UpdateBall(ref nearestBall, mousePosition.xy);
}
}

} else if (Mouse.current.middleButton.isPressed && _ballId != 0) {
if (GetCursorPositionOnPlayfield(out var mousePosition)) {
if (GetCursorPositionOnPlayfield(out var mousePosition, out var _)) {
ref var ball = ref _physicsEngine.BallState(_ballId);
UpdateBall(ref ball, mousePosition);
UpdateBall(ref ball, mousePosition.xy);
}
}

Expand All @@ -112,28 +70,5 @@ private void UpdateBall(ref BallState ballState, float2 position)
ballState.ManualControl = true;
ballState.ManualPosition = position;
}

private bool GetCursorPositionOnPlayfield(out float2 position)
{
if (Camera.main == null) {
position = float2.zero;
return false;
}

var mouseOnScreenPos = Mouse.current.position.ReadValue();
var ray = Camera.main.ScreenPointToRay(mouseOnScreenPos);

if (_playfieldPlane.Raycast(ray, out var enter)) {
var playfieldPosWorld = _playfield.transform.localToWorldMatrix.inverse.MultiplyPoint(ray.GetPoint(enter));
var playfieldPosLocal = _wtl.MultiplyPoint(playfieldPosWorld);

position = new float2(playfieldPosLocal.x, playfieldPosLocal.y);

// todo check playfield bounds
return true;
}
position = float2.zero;
return false;
}
}
}
Loading
Loading