Initial commit

This commit is contained in:
HussienX72u
2026-07-21 08:56:10 +03:00
commit 4017028111
9410 changed files with 2150500 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 86e5793079a8a16478ed6abe3c4b2fbb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c48d0bb4e268084bb4e662e8a7e843c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class AxialMoveBehavior : ToggleBehaviour
{
private SelectionManager selectionManager;
private GameObject currentGizmosMove;
[SerializeField] private GameObject gizmosMovePrefab;
private IInteractable CurrentSelection;
protected void Start()
{
selectionManager = SelectionManager.getInstance();
selectionManager.SelectionEvent.AddListener(interactable => CurrentSelection = interactable);
base.Start();
}
public override void Activate()
{
GizmoInit();
}
public override void Deactivate()
{
GizmoDeselect();
}
private void GizmoInit()
{
if (selectionManager.HasOneSelection())
{
if (currentGizmosMove == null)
{
currentGizmosMove = Instantiate(gizmosMovePrefab, CurrentSelection.transform);
currentGizmosMove.transform.parent = CurrentSelection.transform;
currentGizmosMove.transform.localPosition = Vector3.zero;
currentGizmosMove.transform.localRotation = Quaternion.identity;
}
}
}
private void GizmoDeselect()
{
if (currentGizmosMove != null)
{
Destroy(currentGizmosMove);
currentGizmosMove = null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f7369a4817ce3df499fbd2826b839ebd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IBehaviour
{
void Activate();
void Deactivate();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d29b16d510e417f48b045fc3d1ab28f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public abstract class ToggleBehaviour : MonoBehaviour, IBehaviour
{
[SerializeField] private Toggle toggle;
private bool isActivated = false;
protected virtual void Start()
{
toggle.onValueChanged.AddListener(value => init());
}
private void init()
{
if (isActivated)
Deactivate();
else
Activate();
isActivated = !isActivated; // flip the flag
}
public abstract void Activate();
public abstract void Deactivate();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ce060346105eeb44a18e1c3583bffeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b7b4723e08ae5fc4782baf6f1af7fbaa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1d1103c6a25be6447a68e64d4e21211a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,50 @@
using UnityEngine;
public struct GhostMovementContext
{
// Ghost
public readonly Transform GhostTransform;
// surface
public readonly Vector3 SurfacePoint;
public readonly Vector3 SurfaceNormal;
// Socket snap
public readonly SocketPoint MySocket;
public readonly SocketPoint TargetSocket;
// Per-prefab config
public readonly Transform AttachPoint;
public readonly float FollowSpeed;
public readonly float HeightOffset;
public readonly bool AlignToSurfaceNormal;
public readonly bool SnapRotation;
// Output, GhostManager reads it after the call
public Vector3 RotationAxis;
public GhostMovementContext(
Transform ghostTransform,
Vector3 surfacePoint,
Vector3 surfaceNormal,
SocketPoint mySocket,
SocketPoint targetSocket,
Transform attachPoint,
float followSpeed,
float heightOffset,
bool alignToSurfaceNormal,
bool snapRotation)
{
GhostTransform = ghostTransform;
SurfacePoint = surfacePoint;
SurfaceNormal = surfaceNormal;
MySocket = mySocket;
TargetSocket = targetSocket;
AttachPoint = attachPoint;
FollowSpeed = followSpeed;
HeightOffset = heightOffset;
AlignToSurfaceNormal = alignToSurfaceNormal;
SnapRotation = snapRotation;
RotationAxis = Vector3.up;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c3839bd09a5f994a978d6ec4d28907e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,4 @@
public interface IBlockValidator
{
bool IsValid(BlockEditContext ctx);
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b7bdf5f93500cc4ea122aed8590f119
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
/// <summary>
/// Drives how the ghost object moves and orients each frame.
/// </summary>
public interface IGhostMovementStrategy
{
/// <summary>
/// Called every frame when there is an active ghost.
/// Responsible for updating GhostTransform.position and .rotation.
/// Must also write ctx.RotationAxis before returning.
/// </summary>
void UpdateMovement(ref GhostMovementContext ctx);
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88109b2aac07c13428dd1bb3bbf4663a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
public interface IPlacementHandler
{
/// <summary>
/// Commit the ghost's current transform as the final placed state.
/// </summary>
void Commit(BlockEditContext ctx);
/// <summary>
/// Cancel the edit and restore any previous state.
/// </summary>
void Cancel(BlockEditContext ctx);
void Debugln();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a79e2636d81808f4ab5c5769d6ea3057
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
public interface IRotateHandler
{
/// <summary>
/// handle rotation of Ghost Transfrom
/// </summary>
void performRotation(BlockEditContext ctx);
/// <summary>
/// add an angle to the allowed angles to rotate with
/// </summary>
void addAngle(float angle);
/// <summary>
/// remove that angle
/// </summary>
void removeAngle(float angle);
/// <summary>
/// clear all added angles
/// </summary>
void clearAngles();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83bb80b9424f55842853a57c456c0401
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
public interface ISnapBehavior
{
/// <summary>
/// Called every frame after socket scanning.
/// Returns true if a snap was applied and false if failed.
/// </summary>
bool TrySnap(BlockEditContext ctx);
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74a4d0f44a9d4224f892223405ba68cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,5 @@
public interface IVisualFeedback
{
void OnValid(BlockEditContext ctx);
void OnInvalid(BlockEditContext ctx);
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d02dfbcf6de3142458a6092ebae279bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,7 @@
using System.Runtime.CompilerServices;
// Grant InteractionSystem (which contains GhostManagerV2) access to internal
// members of Scripts.BlockExtensions (which contains BlockEditContext).
// This preserves internal encapsulation against all other assemblies while
// keeping the two tightly-coupled modules able to work together.
[assembly: InternalsVisibleTo("InteractionSystem")]
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 32603667c964c0741a0654170266de51
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,66 @@
using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// Self-contained state for one block being edited (placed or repositioned).
/// </summary>
public class BlockEditContext
{
// Core data
public GameObject GhostObject { get; internal set; }
public Transform GhostTransform => GhostObject != null ? GhostObject.transform : null;
public GameObject SourceObject { get; internal set; } // null for new blocks
public BlockData BlockData { get; internal set; }
public Renderer[] GhostRenderers { get; internal set; }
// visual data
public bool AlignToSurfaceNormal { get; internal set; } = true;
public bool SnapRotation { get; internal set; } = false;
public float FollowSpeed { get; internal set; } = 20f;
public float HeightOffset { get; internal set; } = 0f;
public Color ValidTint { get; internal set; } = new Color(0.5f, 1f, 0.5f, 1f);
public Color InvalidTint { get; internal set; } = new Color(1f, 0.5f, 0.5f, 1f);
public Transform AttachPoint { get; internal set; }
// type data
public bool IsEditingExisting { get; internal set; }
public Vector3 OriginalPosition { get; internal set; }
public Quaternion OriginalRotation { get; internal set; }
public bool IsValidPlacement { get; internal set; } = true;
// editable state
public Vector3 SurfacePoint { get; internal set; }
public Vector3 SurfaceNormal { get; internal set; } = Vector3.up;
public Vector3 RotationAxis { get; internal set; } = Vector3.up;
// sockets
public SocketPoint TargetSocket { get; internal set; }
public SocketPoint MySocket { get; internal set; }
// block references
public Block Block { get; internal set; }
public JointBlock JointBlock { get; internal set; }
public SocketContainer Container { get; internal set; }
// Behaviors
public IGhostMovementStrategy CurrentStrategy { get; set; }
public ISnapBehavior SnapBehavior { get; set; }
public IPlacementHandler PlacementHandler { get; set; }
public IBlockValidator Validator { get; set; }
public IVisualFeedback VisualFeedback { get; set; }
public IRotateHandler RotationHandler { get; set; }
public bool IsActive => GhostObject != null && GhostObject.activeSelf;
public void SetRotationAxis(Vector3 axis) => RotationAxis = axis;
internal void SetFrameState(Vector3 surfacePoint, Vector3 surfaceNormal, SocketPoint mySocket, SocketPoint targetSocket)
{
SurfacePoint = surfacePoint;
SurfaceNormal = surfaceNormal;
MySocket = mySocket;
TargetSocket = targetSocket;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3de2505470ee0ae4e8f2c0bfe9636ab8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f7a1ac0be730984ea7518e191b2fa5b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using UnityEngine;
/// <summary>
/// fallback to collision validator
/// </summary>
public class DefaultBlockValidator : IBlockValidator
{
public bool IsValid(BlockEditContext ctx)
{
IValidator v = ctx.GhostObject.GetComponent<IValidator>();
return v != null ? v.IsValidPlacement(ctx.GhostObject) : true;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4dd246c8854b6164584d9a3f53ce4e45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DefaultRotationHandler : IRotateHandler
{
private List<float> anglesToRotate = new List<float>();
private List<float> addedAngles = new List<float>();
private int currentAngleIndex = 0;
public DefaultRotationHandler()
{
// add regular angles
anglesToRotate.Add(0);
anglesToRotate.Add(45);
anglesToRotate.Add(90);
anglesToRotate.Add(135);
anglesToRotate.Add(180);
anglesToRotate.Add(225);
anglesToRotate.Add(270);
anglesToRotate.Add(315);
}
public void performRotation(BlockEditContext ctx)
{
if (ctx != null && ctx.IsActive)
{
float previousAngle = anglesToRotate[currentAngleIndex];
currentAngleIndex++;
currentAngleIndex %= anglesToRotate.Count;
float currentAngle = anglesToRotate[currentAngleIndex];
float deltaAngle = currentAngle - previousAngle;
Vector3 pivotPoint = ctx.GhostTransform.position;
if (ctx.TargetSocket != null && ctx.MySocket != null)
{
pivotPoint = ctx.MySocket.transform.position;
}
else if (ctx.AttachPoint != null)
{
pivotPoint = ctx.AttachPoint.position;
}
applyRotation(ctx.GhostTransform, pivotPoint, ctx.RotationAxis, deltaAngle);
}
}
public void addAngle(float angle)
{
if (addedAngles.Contains(angle))
{
return;
}
// normalize the angle (0 - 360)
if (angle >= 0)
{
angle = angle % 360;
}
else
{
while (angle < 0)
{
angle += 360;
}
}
int currentSize = anglesToRotate.Count;
// add it to list
for (int i = 0; i < anglesToRotate.Count; i++)
{
if (angle < anglesToRotate[i])
{
anglesToRotate.Insert(i, angle);
return;
}
}
if (currentSize == anglesToRotate.Count) // it's the largest element in the whole rotations
{
anglesToRotate.Add(angle);
}
addedAngles.Add(angle);
}
public void removeAngle(float angle)
{
anglesToRotate.Remove(angle);
addedAngles.Remove(angle);
}
// should be called when changing the object to be snapped to
public void clearAngles()
{
foreach (float angle in addedAngles)
{
anglesToRotate.Remove(angle);
}
addedAngles.Clear();
// reset to the nearest rotation angle
currentAngleIndex++;
currentAngleIndex %= anglesToRotate.Count;
}
private void applyRotation(Transform transform, Vector3 pivot, Vector3 axis, float angle)
{
transform.RotateAround(pivot, axis, angle);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 91311cb2805395b4487594770ab5bca2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
using UnityEngine;
/// <summary>
/// regulare snap performed in free move
/// </summary>
public class DefaultSnapBehavior : ISnapBehavior
{
public bool TrySnap(BlockEditContext ctx)
{
if (ctx.TargetSocket == null) return false;
if (ctx.MySocket != null && !ctx.TargetSocket.CanAccept(ctx.MySocket)) return false;
if (ctx.MySocket != null)
{
// 1. Primary Alignment: Align the exact socket normals
Vector3 myNormal = ctx.MySocket.GetNormal().normalized;
Vector3 targetNormal = -ctx.TargetSocket.GetNormal().normalized;
Quaternion primaryRot = Quaternion.FromToRotation(myNormal, targetNormal);
ctx.GhostTransform.rotation = primaryRot * ctx.GhostTransform.rotation;
// 2. Secondary Alignment: Align the 'Up' (or Forward) vectors to prevent random twisting/rolling
Vector3 mySecondary = Mathf.Abs(Vector3.Dot(ctx.MySocket.transform.up, targetNormal)) < 0.9f
? ctx.MySocket.transform.up
: ctx.MySocket.transform.forward;
Vector3 targetSecondary = Mathf.Abs(Vector3.Dot(ctx.TargetSocket.transform.up, -targetNormal)) < 0.9f
? ctx.TargetSocket.transform.up
: ctx.TargetSocket.transform.forward;
// Project secondary vectors onto the flat plane between the sockets
Vector3 projectedMySecondary = Vector3.ProjectOnPlane(mySecondary, targetNormal).normalized;
Vector3 projectedTargetSecondary = Vector3.ProjectOnPlane(targetSecondary, targetNormal).normalized;
if (projectedMySecondary != Vector3.zero && projectedTargetSecondary != Vector3.zero)
{
// Find the twist angle needed to align the secondary axes
float angle = Vector3.SignedAngle(projectedMySecondary, projectedTargetSecondary, targetNormal);
// Snap to the nearest 45 degrees to allow manual 'R' rotations to persist!
float correction = angle - Mathf.Round(angle / 45f) * 45f;
// Rotate the ghost around its own pivot (since we haven't translated yet)
ctx.GhostTransform.rotation = Quaternion.AngleAxis(correction, targetNormal) * ctx.GhostTransform.rotation;
}
// 3. Translate the ghost so the sockets physically touch
ctx.GhostTransform.position +=
ctx.TargetSocket.transform.position - ctx.MySocket.transform.position;
}
else
{
Transform pivot = ctx.AttachPoint != null ? ctx.AttachPoint : ctx.GhostTransform;
Vector3 pivotOffset = pivot.position - ctx.GhostTransform.position;
ctx.GhostTransform.position = ctx.TargetSocket.transform.position - pivotOffset;
}
return true;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f432ee5d448b4ec409cac4c0ad2b7cd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
using UnityEngine;
/// <summary>
/// Simple material color tint
/// </summary>
public class DefaultVisualFeedback : IVisualFeedback
{
public void OnValid(BlockEditContext ctx) => ApplyTint(ctx, ctx.ValidTint);
public void OnInvalid(BlockEditContext ctx) => ApplyTint(ctx, ctx.InvalidTint);
private static void ApplyTint(BlockEditContext ctx, Color tint)
{
foreach (var r in ctx.GhostRenderers)
if (r != null) r.material.color = tint;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cca890b0c1294ca4b8b8eda4c5667a36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,80 @@
using UnityEngine;
/// <summary>
/// Placement handler for repositioning an already-placed block.
/// </summary>
public class ExistingBlockPlacementHandler : IPlacementHandler
{
private readonly CommandHandler _commandHandler;
public ExistingBlockPlacementHandler(CommandHandler commandHandler)
{
_commandHandler = commandHandler;
Debug.Log("initialize the ExistingBlockPlacement");
}
public void Commit(BlockEditContext ctx)
{
if (ctx.SourceObject == null) return;
_commandHandler.moveBlock(
ctx.SourceObject.GetComponent<Block>().getBlockId(),
ctx.OriginalPosition,
ctx.GhostTransform.position,
ctx.OriginalRotation,
ctx.GhostTransform.rotation);
// ensure the correct positionning after commiting
RestoreObject(ctx, ctx.GhostTransform.position, ctx.GhostTransform.rotation);
}
public void Cancel(BlockEditContext ctx)
{
if (ctx.SourceObject == null) return;
RestoreObject(ctx, ctx.OriginalPosition, ctx.OriginalRotation);
}
private void RestoreObject(BlockEditContext ctx, Vector3 pos, Quaternion rot)
{
ctx.SourceObject.transform.SetPositionAndRotation(pos, rot);
RestoreMaterialAlphaAndColor(ctx.SourceObject);
SetLayerRecursively(ctx.SourceObject, LayerMask.NameToLayer("Interactable"));
ctx.SourceObject.SetActive(true);
}
private void RestoreMaterialAlphaAndColor(GameObject obj)
{
foreach (var r in obj.GetComponentsInChildren<Renderer>())
{
if (r.gameObject.layer == LayerMask.NameToLayer("Gizmos")) continue;
foreach (var mat in r.materials)
{
if (mat.HasProperty("_Color"))
{
mat.color = new Color(1f, 1f, 1f, 1f);
}
}
}
}
private void SetLayerRecursively(GameObject obj, int layer, System.Func<Transform, bool> Method)
{
if (Method(obj.transform))
obj.layer = layer;
foreach (Transform child in obj.transform)
SetLayerRecursively(child.gameObject, layer, Method);
}
private void SetLayerRecursively(GameObject obj, int layer)
{
SetLayerRecursively(obj, layer, x =>
{
return x.gameObject.layer != LayerMask.NameToLayer("Gizmos");
});
}
public void Debugln()
{
Debug.Log("Hi I'm Existing");
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75458c1d52fce984e892d59070e8c68c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
using DependencyInjection.ScriptableObjects;
using UnityEngine;
/// <summary>
/// Concrete ScriptableObject anchor for IPlacementHandler.
/// Injected at runtime so consumers always hold a stable typed reference
/// and can react to handler swaps via the OnValueChanged event.
/// </summary>
[CreateAssetMenu(fileName = "GhostAnchor", menuName = "Dependency Injection/Ghost Anchor")]
public class GhostAnchor : RuntimeAnchor<IPlacementHandler>
{
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41e9786ef7c2c154bad698653e1506c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Generic;
using DependencyInjection;
using UnityEngine;
/// <summary>
/// encapsulate the behaviors implementation and added to the ghost block on runtime
/// </summary>
public class GhostController : MonoBehaviour
{
[RuntimeInject]
private IPlacementHandler placementHandler;
[Inject]
private IBlockValidator blockValidator;
private ISnapBehavior snapBehavior;
private IVisualFeedback visualFeedback;
private IRotateHandler rotationHandler;
private GlopalLogger logger;
[Inject]
public void init(GlopalLogger logger)
{
this.logger = logger;
}
void Start()
{
if (blockValidator == null)
{
logger.Log("Hamada msh Maugod");
}
else
{
logger.Log("Laa Hamada Maugod");
}
if (placementHandler == null)
{
logger.Log("Hamada Eltany msh Maugod");
}
else
{
placementHandler.Debugln();
}
Invoke(nameof(check), 15);
}
void check()
{
if (placementHandler == null)
{
logger.Log("Hamada Eltany msh Maugod");
}
else
{
placementHandler.Debugln();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1495a92f469d22429fde76e6ac478fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
using UnityEngine;
/// <summary>
/// Placement handler for brand-new blocks dragged from the menu.
/// </summary>
public class NewBlockPlacementHandler : IPlacementHandler
{
private readonly CommandHandler _commandHandler;
public NewBlockPlacementHandler(CommandHandler commandHandler)
{
_commandHandler = commandHandler;
Debug.Log("initialize the NewBlockPlacement");
}
public void Commit(BlockEditContext ctx)
{
if (ctx.Block == null) return;
_commandHandler.createBlock(
ctx.Block.getBlockData()?.blockName,
ctx.GhostTransform.position,
ctx.GhostTransform.rotation);
}
public void Cancel(BlockEditContext ctx)
{
// nothing for now
}
public void Debugln()
{
Debug.Log("Hi I'm New");
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e76ae260e459c7b4f931cb9c38a0a562
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using DependencyInjection;
using UnityEngine;
public class Provider : MonoBehaviour, IDependencyProvider
{
[Provide]
public IBlockValidator provideValidator()
{
return new DefaultBlockValidator();
}
[Provide]
public IPlacementHandler providePlacementHandler()
{
Debug.Log("First Handler");
return new NewBlockPlacementHandler(null);
}
public void ChangeAnchorInstance()
{
Injector.Instance.Register<IPlacementHandler>(new ExistingBlockPlacementHandler(null));
Debug.Log("Second Handler");
}
private void Start()
{
Invoke(nameof(ChangeAnchorInstance), 5);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 241c3debd0aeb9840afb1b6b80fe066e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
{
"name": "Scripts.BlockExtensions",
"rootNamespace": "",
"references": [
"GUID:0f8d877125e73cc40a31b031c2e5fe60",
"GUID:b59dd872b9d7ceb4094d9c76ee4f729c",
"GUID:87ecf4ad9955b8f49b5bd39d4f087204",
"GUID:c16923da89c230b46a6c01482752a2ba",
"GUID:932aa9200c814388a2af10406d3eb62e",
"GUID:fc759e1ad51443e5b90aa7b791e33f55",
"GUID:9f27db84ae564425a5b8e82f2f331060"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bbd29cf08aeef3242bbcb0fd3d725c3b
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 43df35c72efc6fc4fb793a9a587cdf12
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Block : MonoBehaviour
{
[SerializeField] private BlockData blockData;
private string blockId;
public abstract void setupBlock();
public abstract void simulate();
public BlockData getBlockData()
{
return blockData;
}
public void SetBlockData(BlockData BD) => blockData = BD;
public void setBlockId(string id)
{
blockId = id;
}
public string getBlockId()
{
return blockId;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 92f0bbe7028183946b24328df29a379b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
using UnityEngine;
[CreateAssetMenu(fileName = "BlockData", menuName = "Blocks/Block Data", order = 51)]
public class BlockData : ScriptableObject
{
[Header("Block Info")]
public string blockName;
public GameObject blockPrefab;
public CategoryData associatedCategory;
[Header("Icon")]
public Texture2D icon;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0f036c260429df458bc55fbece6c077
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
public class BlockFactory : MonoBehaviour
{
private static BlockFactory Instance;
private Environment environment;
private List<BlockData> blockDataList;
private Dictionary<string, BlockData> blocksData = new Dictionary<string, BlockData>();
public static BlockFactory getInstance()
{
if (Instance == null)
{
GameObject factoryObject = new GameObject("blockFactory");
Instance = factoryObject.AddComponent<BlockFactory>();
}
return Instance;
}
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
environment = Environment.getInstance();
blockDataList = Resources.LoadAll<BlockData>("BlocksData").ToList();
foreach (BlockData blockData in blockDataList)
{
blocksData.Add(blockData.blockName, blockData);
}
}
public GameObject createblock(string prefabId, Vector3 position, Quaternion rotation, string id = null, bool layerDiff = false)
{
Debug.Log(prefabId);
if (blocksData.ContainsKey(prefabId))
{
GameObject blockPrefab = blocksData[prefabId].blockPrefab;
GameObject block = Instantiate(blockPrefab, position, rotation);
if (layerDiff)
{
block.gameObject.layer = LayerMask.NameToLayer("Ignore Interaction");
Debug.Log($"Set layer of {block} to {LayerMask.LayerToName(block.layer)}");
}
// the only blocks added to the environment is created with BlockFactory
environment.addBlock(block.GetComponent<Block>(), id);
return block;
}
else
{
Debug.LogWarning($"block with PrefabId {prefabId} not found!");
return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6119bef9c1e159341b755fad6f48329c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "ItemSystem",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0f8d877125e73cc40a31b031c2e5fe60
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "Category", menuName = "YoussefScripts/Create a Category")]
public class CategoryData : ScriptableObject
{
public string ID;
public List<BlockData> blocks;
public Sprite icon;
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec390037e79ae134b9fe859322a98be8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CodingBlock : Block
{
public override void setupBlock()
{
throw new System.NotImplementedException();
}
public override void simulate()
{
throw new System.NotImplementedException();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1aca36b1d6467654a9a832b13390ed0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ElectricalBlock : Block
{
public override void setupBlock()
{
throw new System.NotImplementedException();
}
public override void simulate()
{
throw new System.NotImplementedException();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10902277cb1a7434ebcb6c50edc00c52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,117 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
public class Environment : MonoBehaviour
{
private static Environment instance;
private Dictionary<string, Block> blockRegistry = new Dictionary<string, Block>();
public UnityEvent<Block> onBlockAddedEvent;
public UnityEvent<Block, Vector3, Quaternion> onBlockMovedEvt;
public UnityEvent<Block> onBlockRemovedEvent;
public UnityEvent onChangeEvent;
public static Environment getInstance()
{
if (instance == null)
{
instance = FindObjectOfType<Environment>();
if (instance == null)
{
GameObject environmentObject = new GameObject("Environment");
instance = environmentObject.AddComponent<Environment>();
}
}
return instance;
}
public Dictionary<String, Block> getBlockRegistry()
{
return blockRegistry;
}
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
}
public void addBlock(Block block, string existingUUID = null)
{
// 1. Assign ID: Use existing (Undo/Redo) or generate a new one (Fresh placement)
string uuid = string.IsNullOrEmpty(existingUUID) ? Guid.NewGuid().ToString() : existingUUID;
block.setBlockId(uuid);
// 2. Register in the dictionary
if (!blockRegistry.ContainsKey(uuid))
{
blockRegistry.Add(uuid, block);
}
else
{
blockRegistry[uuid] = block; // Update reference if already exists
}
applyChanges();
onBlockAddedEvent.Invoke(block);
}
public void moveBlock(Block block, Vector3 oldPos, Quaternion oldRot, Vector3 newPos, Quaternion newRot)
{
block.transform.SetPositionAndRotation(newPos, newRot);
onBlockMovedEvt.Invoke(block, oldPos, oldRot);
applyChanges();
}
public void removeBlock(string uuid)
{
if (blockRegistry.TryGetValue(uuid, out Block block))
{
onBlockRemovedEvent.Invoke(block);
blockRegistry.Remove(uuid);
applyChanges();
}
}
public void applyChanges()
{
onChangeEvent.Invoke();
}
public void completeDestroyBlock(Block block)
{
if (block != null)
Destroy(block.gameObject);
}
public Block getBlock(string uuid)
{
if (blockRegistry.TryGetValue(uuid, out Block block))
{
return block;
}
return null;
}
public void clearEnvironment()
{
foreach (var blockEntry in blockRegistry)
{
if (blockEntry.Value != null)
{
Destroy(blockEntry.Value.gameObject);
}
}
blockRegistry.Clear();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f8e7f129da58b9746a67217bcf1a4aa9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MechanicalBlock : Block
{
public override void setupBlock()
{
}
public override void simulate()
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4a1be166f8c7184ab860f9a010f5764
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MechanicalJoint : Block
{
public override void setupBlock()
{
// unimplemented as it's a Joint
}
public override void simulate()
{
throw new System.NotImplementedException();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2a09709a730033f4986af4e51fe685b2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 626a4b0a2a746e148bda3a6624d0c7ff
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,212 @@
using System.Collections;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
[Header("Dependencies")]
private BaseInputProvider inputProvider;
[Header("Settings")]
public Transform pivotPoint;
public LayerMask obstacleLayer;
[Header("Speeds")]
public float panSpeed = 0.5f;
public float rotateSpeed = 5.0f;
public float zoomSpeed = 5.0f;
public float minZoom = 2.0f;
public float maxZoom = 50.0f;
[Header("Smoothness")]
public bool enableSmoothing = true;
public float smoothTime = 0.1f;
// ---- Internal State ----
private Vector3 _targetPosition;
private Quaternion _targetRotation;
private float _targetZoom;
private Vector3 _currentVelocity;
private bool _isPanning;
private bool _isOrbiting;
private Vector2 _cursorScreenPos; // kept up to date by OnCursorMoved
private Camera _cam;
public bool isInputLocked = false;
private bool isInitialized = false;
// -------------------------------------------------------
// Lifecycle
// -------------------------------------------------------
private void Start()
{
_cam = GetComponent<Camera>();
if (pivotPoint == null)
{
var pivotObj = new GameObject("CameraPivot");
pivotObj.transform.position = transform.position + transform.forward * 10f;
pivotPoint = pivotObj.transform;
}
_targetPosition = pivotPoint.position;
_targetRotation = pivotPoint.rotation;
_targetZoom = Vector3.Distance(transform.position, pivotPoint.position);
inputProvider = InputProviderManager.getInstance()?.getCurrentInputProvider();
inputProvider.OnCursorMoved.AddListener(OnCursorMoved);
inputProvider.OnCursorDelta.AddListener(OnCursorDelta);
inputProvider.OnPanStart.AddListener(OnPanStart);
inputProvider.OnPanEnd.AddListener(OnPanEnd);
inputProvider.OnRotateStart.AddListener(OnRotateStart);
inputProvider.OnRotateEnd.AddListener(OnRotateEnd);
inputProvider.OnZoom.AddListener(OnZoom);
isInitialized = true;
}
private void OnEnable()
{
if (!isInitialized) return;
inputProvider.OnCursorMoved.AddListener(OnCursorMoved);
inputProvider.OnCursorDelta.AddListener(OnCursorDelta);
inputProvider.OnPanStart.AddListener(OnPanStart);
inputProvider.OnPanEnd.AddListener(OnPanEnd);
inputProvider.OnRotateStart.AddListener(OnRotateStart);
inputProvider.OnRotateEnd.AddListener(OnRotateEnd);
inputProvider.OnZoom.AddListener(OnZoom);
}
private void OnDisable()
{
inputProvider.OnCursorMoved.RemoveListener(OnCursorMoved);
inputProvider.OnCursorDelta.RemoveListener(OnCursorDelta);
inputProvider.OnPanStart.RemoveListener(OnPanStart);
inputProvider.OnPanEnd.RemoveListener(OnPanEnd);
inputProvider.OnRotateStart.RemoveListener(OnRotateStart);
inputProvider.OnRotateEnd.RemoveListener(OnRotateEnd);
inputProvider.OnZoom.RemoveListener(OnZoom);
}
private void LateUpdate() => ApplyMovement();
// -------------------------------------------------------
// Input event handlers
// -------------------------------------------------------
private void OnCursorMoved(Vector2 screenPos)
{
_cursorScreenPos = screenPos;
}
private void OnPanStart(Vector2 cursorScreenPos)
{
if (isInputLocked) return;
Ray ray = _cam.ScreenPointToRay(cursorScreenPos);
_isPanning = !Physics.Raycast(ray, out _, 1000f, obstacleLayer);
}
private void OnPanEnd() => _isPanning = false;
private void OnRotateStart()
{
if (!isInputLocked) _isOrbiting = true;
}
private void OnRotateEnd() => _isOrbiting = false;
private void OnCursorDelta(Vector2 delta)
{
if (_isPanning)
{
if (!InputModeManager.Is(InputMode.Camera)) return;
Vector3 move = -transform.right * (delta.x * panSpeed * 0.01f)
+ -transform.up * (delta.y * panSpeed * 0.01f);
_targetPosition += move;
}
if (_isOrbiting)
{
float mouseX = delta.x * rotateSpeed;
float mouseY = -delta.y * rotateSpeed;
Vector3 euler = _targetRotation.eulerAngles;
euler.y += mouseX;
euler.x += mouseY;
if (euler.x > 180f) euler.x -= 360f;
euler.x = Mathf.Clamp(euler.x, -85f, 85f);
_targetRotation = Quaternion.Euler(euler);
}
}
private void OnZoom(float scrollValue)
{
if (!InputModeManager.Is(InputMode.Camera)) return;
_targetZoom -= scrollValue * zoomSpeed * 0.05f;
_targetZoom = Mathf.Clamp(_targetZoom, minZoom, maxZoom);
}
// -------------------------------------------------------
// Movement application (unchanged logic)
// -------------------------------------------------------
private void ApplyMovement()
{
if (enableSmoothing)
{
pivotPoint.position = Vector3.SmoothDamp(
pivotPoint.position, _targetPosition, ref _currentVelocity, smoothTime);
pivotPoint.rotation = Quaternion.Slerp(
pivotPoint.rotation, _targetRotation, Time.deltaTime * (1f / smoothTime));
}
else
{
pivotPoint.position = _targetPosition;
pivotPoint.rotation = _targetRotation;
}
transform.position = pivotPoint.position - pivotPoint.forward * _targetZoom;
transform.LookAt(pivotPoint);
}
// -------------------------------------------------------
// Public API
// -------------------------------------------------------
public void SnapToDirection(Vector3 direction)
{
Vector3 cameraUp = Mathf.Abs(Vector3.Dot(direction, Vector3.up)) > 0.99f
? Vector3.forward
: Vector3.up;
_targetRotation = Quaternion.LookRotation(direction, cameraUp);
_targetPosition = pivotPoint.position;
_currentVelocity = Vector3.zero;
}
// -------------------------------------------------------
// Gizmos
// -------------------------------------------------------
private void OnDrawGizmos()
{
if (pivotPoint == null) return;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(pivotPoint.position, 0.5f);
Gizmos.DrawLine(transform.position, pivotPoint.position);
Gizmos.color = Color.cyan;
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
Gizmos.DrawFrustum(Vector3.zero, GetComponent<Camera>().fieldOfView, maxZoom, minZoom, 1.0f);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ff809502f69bf4419a49383fe2deff4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
{
"name": "Scripts.Camera",
"rootNamespace": "",
"references": [
"GUID:d5be31af97e8e5e458cc6c5d560bb7cc"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7239d94c510c9fd4da3ba5963eadd5d6
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,132 @@
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class ViewCubeController : MonoBehaviour
{
[Header("References")]
public Camera mainCam; // The Base Camera
public Camera viewCubeCam; // The Overlay Camera/Texture Camera
private CameraControl CameraController; // Your camera movement script
public RawImage viewCubeImage;
private EventTrigger eventTrigger;
private bool isPointerInViewCube = false;
[Header("Settings")]
private Transform cubeContainer;
public LayerMask viewCubeLayer;
// NEW: We need the RectTransform to calculate positions correctly
private RectTransform rawImageRect;
// NEW: Store the last ray for debugging in Gizmos
private Ray debugRay;
private void Start()
{
cubeContainer = transform;
if (mainCam) CameraController = mainCam.GetComponent<CameraControl>();
// Cache the RectTransform so we can access width/height later
rawImageRect = viewCubeImage.GetComponent<RectTransform>();
eventTrigger = viewCubeImage.GetComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerEnter;
entry.callback.AddListener((data) => { isPointerInViewCube = true; });
eventTrigger.triggers.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerExit;
entry.callback.AddListener((data) => { isPointerInViewCube = false; });
eventTrigger.triggers.Add(entry);
}
private void Update()
{
RotateCube();
if (CameraController != null)
{
CameraController.isInputLocked = isPointerInViewCube;
}
if (isPointerInViewCube)
{
CalculateCurrentRay();
}
DetectClick();
}
void RotateCube()
{
if (mainCam == null || cubeContainer == null) return;
cubeContainer.rotation = mainCam.transform.rotation;
}
// NEW FUNCTION: Handles the math to convert Screen Pixels -> UI Pixels -> Camera Ray
void CalculateCurrentRay()
{
Vector2 localPoint;
// 1. Convert Screen Mouse Point to a point inside the RawImage rectangle
// The 'null' param works for "Screen Space - Overlay" canvas.
// If your canvas is "Screen Space - Camera", pass the UI Camera instead of null.
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
rawImageRect,
Input.mousePosition,
null,
out localPoint))
{
// 2. Normalize positions to 0-1 range (Viewport coordinates)
// LocalPoint (0,0) is the center of the image. We shift it by +0.5 to make (0,0) the bottom-left.
float normalizedX = (localPoint.x / rawImageRect.rect.width) + 0.5f;
float normalizedY = (localPoint.y / rawImageRect.rect.height) + 0.5f;
// 3. Create the ray from the ViewCube Camera using these coordinates
debugRay = viewCubeCam.ViewportPointToRay(new Vector3(normalizedX, normalizedY, 0));
}
}
void DetectClick()
{
if (Input.GetMouseButtonDown(0) && isPointerInViewCube)
{
// We use the ray calculated in CalculateCurrentRay()
if (Physics.Raycast(debugRay, out RaycastHit hit, 100f, viewCubeLayer))
{
Debug.Log("View Cube Hit: " + hit.collider.name);
Vector3 targetDir = Vector3.zero;
switch (hit.collider.name)
{
case "Front": targetDir = Vector3.forward; break;
case "Back": targetDir = Vector3.back; break;
case "Left": targetDir = Vector3.left; break;
case "Right": targetDir = Vector3.right; break;
case "Top": targetDir = Vector3.up; break;
case "Bottom": targetDir = Vector3.down; break;
}
if (targetDir != Vector3.zero && CameraController != null)
{
CameraController.SnapToDirection(targetDir);
}
}
}
}
// DRAW GIZMOS: This will now draw the ray based on where your mouse is hovering
private void OnDrawGizmos()
{
if (viewCubeCam == null) return;
Gizmos.color = Color.red;
// Draw the ray stored in debugRay
Gizmos.DrawLine(debugRay.origin, debugRay.origin + debugRay.direction * 100f);
// Draw a small sphere at the ray start point to confirm it's coming from the camera
Gizmos.DrawSphere(debugRay.origin, 0.2f);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1705a4f5a2431a740a1ff509d1051cc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3a976f37bc8f63e4bb7499c81fc84d15
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,61 @@
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CommandDTO
{
[JsonProperty]
private string commandName;
[JsonProperty]
private Dictionary<string, object> parameters = new Dictionary<string, object>();
public string CommandName => commandName;
public Dictionary<string, object> Parameters => parameters;
public ICommand converToCommand()
{
// Implementation for converting DTO to Command
switch (commandName)
{
case "CreateCommand":
return new CreateBlockCommand(this);
case "MoveCommand":
return new MoveBlockCommand(this);
case "RotateCommand":
return new RotateblockCommand(this);
case "DeleteCommand":
return new DeleteBlockCommand(this);
case "DeleteGroup":
return new DeleteGroupCommand(this);
default:
Debug.LogWarning($"Unknown command name: {commandName}");
return null;
}
}
public class CommandDTOBuilder
{
private string commandName;
private Dictionary<string, object> parameters = new Dictionary<string, object>();
public CommandDTOBuilder SetCommandName(string name)
{
this.commandName = name;
return this;
}
public CommandDTOBuilder AddParameter(string key, object value)
{
parameters[key] = value;
return this;
}
public CommandDTO Build()
{
CommandDTO commandDTO = new CommandDTO();
commandDTO.commandName = this.commandName;
commandDTO.parameters = this.parameters;
return commandDTO;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c59b88464539e84c87b396c1cae0b89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,111 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class CommandHandler : MonoBehaviour
{
private static CommandHandler instance;
private CommandManager commandManager;
private Environment environment;
public static CommandHandler getInstance()
{
if (instance == null)
{
instance = FindObjectOfType<CommandHandler>();
if (instance == null)
{
GameObject commandHandlerObject = new GameObject("CommandHandler");
instance = commandHandlerObject.AddComponent<CommandHandler>();
}
}
return instance;
}
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
commandManager = CommandManager.getInstance();
environment = Environment.getInstance();
}
private void Update()
{
// Detect Ctrl+Z for undo and Ctrl+Y for redo
bool ctrl = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
if (ctrl)
{
if (Input.GetKeyDown(KeyCode.Z))
{
Debug.Log("Ctrl+Z detected: Undo action");
undoAction();
}
else if (Input.GetKeyDown(KeyCode.Y))
{
Debug.Log("Ctrl+Y detected: Redo action");
redoAction();
}
}
}
public void createBlock(string prefabId, Vector3 position, Quaternion rotation)
{
ICommand createCommand = new CreateBlockCommand(prefabId, position, rotation);
commandManager.executeCommand(createCommand);
}
public void moveBlock(string blockId, Vector3 prePosition, Vector3 newPosition, Quaternion preRotation, Quaternion newRotation)
{
ICommand moveCommand = new MoveBlockCommand(blockId, prePosition, newPosition, preRotation, newRotation);
commandManager.executeCommand(moveCommand);
}
public void removeBlock(string blockId)
{
ICommand deleteCommand = new DeleteBlockCommand(blockId);
commandManager.executeCommand(deleteCommand);
}
public void removeGroup(List<string> blockIds)
{
ICommand deleteCommand = new DeleteGroupCommand(blockIds);
commandManager.executeCommand(deleteCommand);
}
public void rotateBlock(string blockId, Quaternion newRotation)
{
ICommand rotateCommand = new RotateblockCommand(blockId, newRotation);
commandManager.executeCommand(rotateCommand);
}
public void undoAction()
{
commandManager.undo();
}
public void redoAction()
{
commandManager.redo();
}
public void clearHistory()
{
commandManager.clearHistory();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8802da992aedca34181376075fc0c011
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,73 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CommandManager
{
private static CommandManager instance;
private LinkedList<ICommand> commandStack = new LinkedList<ICommand>();
private LinkedList<ICommand> redoStack = new LinkedList<ICommand>();
private int maxCapacity = 100; // added to prevent memory leak in the long run
private CommandManager()
{
}
public static CommandManager getInstance()
{
if (instance == null)
{
instance = new CommandManager();
}
return instance;
}
public void setMaxCapacity(int capacity)
{
maxCapacity = capacity;
}
public void executeCommand(ICommand command)
{
command.execute();
commandStack.AddFirst(command);
redoStack.Clear();
if (commandStack.Count > maxCapacity)
{
commandStack.RemoveLast();
}
}
public void undo()
{
if (commandStack.Count > 0)
{
ICommand command = commandStack.First.Value;
commandStack.RemoveFirst();
command.undo();
redoStack.AddFirst(command);
}
}
public ICommand redo()
{
if (redoStack.Count > 0)
{
ICommand command = redoStack.First.Value;
redoStack.RemoveFirst();
command.execute();
commandStack.AddFirst(command);
return command;
}
return null;
}
public void clearHistory()
{
commandStack.Clear();
redoStack.Clear();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55e3134ede92000488aa626c2beb2d62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
{
"name": "CommandSystem",
"rootNamespace": "",
"references": [
"GUID:0f8d877125e73cc40a31b031c2e5fe60"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 87ecf4ad9955b8f49b5bd39d4f087204
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96d25cd0bae776b4082ba6691f9f0d52
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,65 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateBlockCommand : ICommand
{
private string prefabId;
private Vector3 position;
private Quaternion rotation;
private GameObject objectCreated;
private string blockId;
private bool layerDiff;
private BlockFactory factory;
private Environment environment;
public CreateBlockCommand(string prefabId, Vector3 position, Quaternion rotation, bool LayerDiff = false)
{
this.prefabId = prefabId;
this.position = position;
this.rotation = rotation;
this.layerDiff = LayerDiff;
this.factory = BlockFactory.getInstance();
this.environment = Environment.getInstance();
}
public CreateBlockCommand(CommandDTO commandDTO)
{
this.prefabId = commandDTO.Parameters["prefabId"].ToString();
this.position = (Vector3)commandDTO.Parameters["position"];
this.rotation = (Quaternion)commandDTO.Parameters["rotation"];
this.layerDiff = (bool)commandDTO.Parameters["layerDiff"];
this.factory = BlockFactory.getInstance();
this.environment = Environment.getInstance();
}
public void execute()
{
objectCreated = factory.createblock(prefabId, position, rotation, blockId, layerDiff);
if (objectCreated != null)
{
Block blockComponent = objectCreated.GetComponent<Block>();
blockId = blockComponent.getBlockId();
}
else
{
Debug.LogWarning("CreateblockCommand: Could not create block with prefab ID " + prefabId);
}
}
public void undo()
{
environment.removeBlock(blockId);
}
public CommandDTO toDTO()
{
return new CommandDTO.CommandDTOBuilder()
.SetCommandName("CreateCommand")
.AddParameter("prefabId", prefabId)
.AddParameter("position", position)
.AddParameter("rotation", rotation)
.AddParameter("layerDiff", layerDiff)
.Build();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6846488550ca76e49ab5a48c410f5f00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DeleteBlockCommand : ICommand
{
private string blockId;
private string prefabDeletedId;
private Vector3 blockInitialPosition;
private Quaternion blockInitialRotation;
private BlockFactory factory;
private Environment environment;
public DeleteBlockCommand(string blockId)
{
this.blockId = blockId;
this.factory = BlockFactory.getInstance();
this.environment = Environment.getInstance();
}
public DeleteBlockCommand(CommandDTO commandDTO)
{
this.blockId = commandDTO.Parameters["blockId"].ToString();
this.factory = BlockFactory.getInstance();
this.environment = Environment.getInstance();
}
public void execute()
{
Block block = environment.getBlock(blockId);
if (block != null)
{
blockInitialPosition = block.transform.position;
blockInitialRotation = block.transform.rotation;
prefabDeletedId = block.getBlockData()?.blockName;
environment.removeBlock(blockId);
}
else
{
Debug.LogWarning("DeleteblockCommand: block with ID " + blockId + " not found.");
}
}
public void undo()
{
factory.createblock(prefabDeletedId, blockInitialPosition, blockInitialRotation, blockId);
}
public CommandDTO toDTO()
{
return new CommandDTO.CommandDTOBuilder()
.SetCommandName("DeleteCommand")
.AddParameter("blockId", blockId)
.Build();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8631dad80687e2b45a2d8ec6ca40f879
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,50 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DeleteGroupCommand : ICommand
{
private List<string> blocksId;
private List<DeleteBlockCommand> deleteBlockCommands;
public DeleteGroupCommand(List<string> blocksId)
{
this.blocksId = blocksId;
deleteBlockCommands = new List<DeleteBlockCommand>();
}
public DeleteGroupCommand(CommandDTO commandDTO)
{
this.blocksId = (List<string>)commandDTO.Parameters["blocksId"];
deleteBlockCommands = new List<DeleteBlockCommand>();
}
public void execute()
{
deleteBlockCommands.Clear();
foreach (string id in blocksId)
{
DeleteBlockCommand command = new DeleteBlockCommand(id);
command.execute();
deleteBlockCommands.Add(command);
}
}
public void undo()
{
foreach (DeleteBlockCommand command in deleteBlockCommands)
{
command.undo();
}
}
public CommandDTO toDTO()
{
return new CommandDTO.CommandDTOBuilder()
.SetCommandName("DeleteGroup")
.AddParameter("blocksId", blocksId)
.Build();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e09c34ca40233944e8c2702acea7eb90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,6 @@
public interface ICommand
{
void execute();
void undo();
CommandDTO toDTO();
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: da8cfd5f6fb13e2479900c9d4caafa4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveBlockCommand : ICommand
{
private string blockId;
private Vector3 previousPosition;
private Quaternion previousRotation;
private Vector3 newPosition;
private Quaternion newRotation;
private Environment environment;
private Transform blockTransform;
public MoveBlockCommand(string blockId, Vector3 previousPosition, Vector3 newPosition, Quaternion previousRotation, Quaternion newRotation)
{
this.blockId = blockId;
this.newPosition = newPosition;
this.previousPosition = previousPosition;
this.environment = Environment.getInstance();
this.previousRotation = previousRotation;
this.newRotation = newRotation;
}
public MoveBlockCommand(CommandDTO commandDTO)
{
this.blockId = commandDTO.Parameters["blockId"].ToString();
this.newPosition = (Vector3)commandDTO.Parameters["newPosition"];
this.previousPosition = (Vector3)commandDTO.Parameters["previousPosition"];
this.newRotation = (Quaternion)commandDTO.Parameters["newRotation"];
this.previousRotation = (Quaternion)commandDTO.Parameters["previousRotation"];
this.environment = Environment.getInstance();
}
public void execute()
{
Block block = environment.getBlock(blockId);
if (block != null)
environment.moveBlock(block, previousPosition, previousRotation, newPosition, newRotation);
else
Debug.LogWarning("MoveblockCommand: block with ID " + blockId + " not found.");
}
public void undo()
{
Block block = environment.getBlock(blockId);
if (block != null)
environment.moveBlock(block, newPosition, newRotation, previousPosition, previousRotation);
else
Debug.LogWarning("MoveblockCommand: block with ID " + blockId + " not found.");
}
public CommandDTO toDTO()
{
return new CommandDTO.CommandDTOBuilder()
.SetCommandName("MoveCommand")
.AddParameter("blockId", blockId)
.AddParameter("newPosition", newPosition)
.AddParameter("previousPosition", previousPosition)
.AddParameter("newRotation", newRotation)
.AddParameter("previousRotation", previousRotation)
.Build();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb9209f3464c8224eb210299f2a742ea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateblockCommand : ICommand
{
private string blockId;
private Quaternion newRotation;
private Quaternion previousRotation;
private Environment environment;
private Transform blockTransform;
public RotateblockCommand(string blockId, Quaternion newRotation)
{
this.blockId = blockId;
this.newRotation = newRotation;
this.environment = Environment.getInstance();
}
public RotateblockCommand(CommandDTO commandDTO)
{
this.blockId = commandDTO.Parameters["blockId"].ToString();
this.newRotation = (Quaternion)commandDTO.Parameters["newRotation"];
this.environment = Environment.getInstance();
}
public void execute()
{
blockTransform = environment.getBlock(blockId)?.transform;
if (blockTransform != null)
{
previousRotation = blockTransform.rotation;
blockTransform.rotation = newRotation;
}
else
{
Debug.LogWarning("RotateblockCommand: block with ID " + blockId + " not found.");
}
}
public void undo()
{
blockTransform = environment.getBlock(blockId)?.transform;
if (blockTransform != null)
{
blockTransform.rotation = previousRotation;
}
}
public CommandDTO toDTO()
{
return new CommandDTO.CommandDTOBuilder()
.SetCommandName("RotateCommand")
.AddParameter("blockId", blockId)
.AddParameter("newRotation", newRotation)
.Build();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8100c36e75da6e42a8d788015b34543
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0403ada0c6e91be46bab191ffa05c627
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More