Initial commit: Unity WordConnect project
This commit is contained in:
@ -0,0 +1,748 @@
|
||||
// // ©2015 - 2025 Candy Smith
|
||||
// // All rights reserved
|
||||
// // Redistribution of this software is strictly not allowed.
|
||||
// // Copy of this software can be obtained from unity asset store only.
|
||||
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// // THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using WordsToolkit.Scripts.Gameplay.Managers;
|
||||
using WordsToolkit.Scripts.GUI;
|
||||
using WordsToolkit.Scripts.Levels;
|
||||
using WordsToolkit.Scripts.System;
|
||||
using TMPro;
|
||||
using UnityEngine.Serialization;
|
||||
using VContainer;
|
||||
using WordsToolkit.Scripts.Audio;
|
||||
using WordsToolkit.Scripts.Enums;
|
||||
using WordsToolkit.Scripts.GUI.Buttons;
|
||||
using WordsToolkit.Scripts.Infrastructure.Service;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.UI;
|
||||
|
||||
namespace WordsToolkit.Scripts.Gameplay
|
||||
{
|
||||
public class WordSelectionManager : MonoBehaviour, IFadeable
|
||||
{
|
||||
// Event for when selected letters change
|
||||
public event Action<List<LetterButton>> OnSelectionChanged;
|
||||
// Event for when selection is completed
|
||||
public event Action<string> OnSelectionCompleted;
|
||||
|
||||
[Header("References")]
|
||||
public FieldManager fieldManager;
|
||||
public LetterButton letterButtonPrefab;
|
||||
|
||||
[Header("Circle Layout Settings")]
|
||||
public float radius = 200f;
|
||||
public Vector2 circleCenter = Vector2.zero;
|
||||
|
||||
private List<LetterButton> selectedLetters = new List<LetterButton>();
|
||||
private bool isSelecting = false;
|
||||
private Vector2 currentMousePosition;
|
||||
private VirtualMouseInput virtualMouseInput;
|
||||
|
||||
private float parallelThreshold = 0.9f; // Dot product threshold for small angle detection (0.9 = ~25 degrees)
|
||||
private float minBackwardDistance = 100f; // Minimum distance to move backward before unselecting
|
||||
private float maxAngleForUnselect = .5f; // Maximum angle in degrees between lines to trigger unselect
|
||||
private bool isGameWon = false;
|
||||
|
||||
public bool IsSelecting => isSelecting;
|
||||
|
||||
public UILineRenderer lineRenderer;
|
||||
[SerializeField]
|
||||
private Transform parentLetters;
|
||||
[SerializeField]
|
||||
private CanvasGroup panelCanvasGroup;
|
||||
|
||||
[Header("UI References")]
|
||||
[SerializeField] private float characterSpacing = 30f; // Spacing between characters
|
||||
public Image backgroundSelectedWord; // Reference to the background image
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI selectedWordText;
|
||||
|
||||
[SerializeField]
|
||||
private HorizontalLayoutGroup layout;
|
||||
|
||||
[Header("Rearrange Settings")]
|
||||
[SerializeField] private float swapAnimationDuration = 0.5f;
|
||||
[SerializeField] private Ease swapAnimationEase = Ease.InOutQuad;
|
||||
|
||||
[Header("Shuffle Button")]
|
||||
[SerializeField] private Button shuffleButton;
|
||||
|
||||
private GameManager gameManager;
|
||||
|
||||
private ILevelLoaderService levelLoaderService;
|
||||
private IAudioService soundBase;
|
||||
[SerializeField]
|
||||
private CanvasGroup canvasGroup;
|
||||
|
||||
[Inject]
|
||||
public void Construct(ILevelLoaderService levelLoaderService, GameManager gameManager, IAudioService soundBase, ButtonViewController buttonViewController)
|
||||
{
|
||||
this.soundBase = soundBase;
|
||||
this.gameManager = gameManager;
|
||||
this.levelLoaderService = levelLoaderService;
|
||||
this.levelLoaderService.OnLevelLoaded += OnLevelLoaded;
|
||||
buttonViewController.RegisterButton(this);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Try to find VirtualMouseInput component in the scene
|
||||
virtualMouseInput = FindObjectOfType<VirtualMouseInput>();
|
||||
OnSelectionCompleted += ValidateWordWithModel;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (shuffleButton != null)
|
||||
{
|
||||
shuffleButton.onClick.AddListener(RearrangeRandomLetters);
|
||||
}
|
||||
|
||||
if (fieldManager != null)
|
||||
{
|
||||
fieldManager.OnAllTilesOpened.AddListener(OnGameWon);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (shuffleButton != null)
|
||||
{
|
||||
shuffleButton.onClick.RemoveListener(RearrangeRandomLetters);
|
||||
}
|
||||
if (levelLoaderService != null)
|
||||
{
|
||||
levelLoaderService.OnLevelLoaded -= OnLevelLoaded;
|
||||
}
|
||||
if (fieldManager != null)
|
||||
{
|
||||
fieldManager.OnAllTilesOpened.RemoveListener(OnGameWon);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Update mouse position and line renderer during selection
|
||||
if (isSelecting)
|
||||
{
|
||||
// Check if input is still active, if not, end selection
|
||||
if (!IsAnyInputActive())
|
||||
{
|
||||
EndSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMousePosition();
|
||||
CheckForBackwardMovement();
|
||||
UpdateLineRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckForBackwardMovement()
|
||||
{
|
||||
if (selectedLetters.Count < 2) return;
|
||||
|
||||
// Get the current line segment (from last selected letter to mouse position)
|
||||
Vector2 lastLetterPos = selectedLetters[selectedLetters.Count - 1].GetComponent<RectTransform>().anchoredPosition;
|
||||
Vector2 secondLastLetterPos = selectedLetters[selectedLetters.Count - 2].GetComponent<RectTransform>().anchoredPosition;
|
||||
Vector2 currentMovement = currentMousePosition - lastLetterPos;
|
||||
|
||||
// Calculate minBackwardDistance as half the distance between the two latest letters
|
||||
float distanceBetweenLatest = Vector2.Distance(lastLetterPos, secondLastLetterPos);
|
||||
float dynamicMinBackwardDistance = distanceBetweenLatest * 0.5f;
|
||||
|
||||
// Check if we've moved back far enough to consider unselecting
|
||||
if (currentMovement.magnitude < dynamicMinBackwardDistance) return;
|
||||
|
||||
// Check only the most recent line segment for backward movement
|
||||
if (selectedLetters.Count >= 2)
|
||||
{
|
||||
int i = selectedLetters.Count - 2; // Only check the last segment
|
||||
Vector2 letterPos = selectedLetters[i].GetComponent<RectTransform>().anchoredPosition;
|
||||
Vector2 previousSegment = selectedLetters[i + 1].GetComponent<RectTransform>().anchoredPosition - letterPos;
|
||||
|
||||
// Check if current movement is backward and at small angle to previous segment
|
||||
if (IsMovingBackwardWithSmallAngle(currentMovement, previousSegment, letterPos))
|
||||
{
|
||||
// Unselect letters from this point forward
|
||||
UnselectLettersFromIndex(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMovingBackwardWithSmallAngle(Vector2 currentMovement, Vector2 previousSegment, Vector2 segmentStart)
|
||||
{
|
||||
// Normalize the segments
|
||||
Vector2 currentDir = currentMovement.normalized;
|
||||
Vector2 prevDir = previousSegment.normalized;
|
||||
|
||||
// Calculate the angle between the directions
|
||||
float dotProduct = Vector2.Dot(currentDir, prevDir);
|
||||
float angleInRadians = Mathf.Acos(Mathf.Clamp(dotProduct, -1f, 1f));
|
||||
float angleInDegrees = angleInRadians * Mathf.Rad2Deg;
|
||||
|
||||
// Check if we're moving backward (opposite direction) with small angle
|
||||
bool isOppositeDirection = dotProduct < 0; // Negative dot product means opposite directions
|
||||
bool isSmallAngle = angleInDegrees > (180f - maxAngleForUnselect) && angleInDegrees < (180f + maxAngleForUnselect);
|
||||
|
||||
return isOppositeDirection && isSmallAngle;
|
||||
}
|
||||
|
||||
private void UnselectLettersFromIndex(int fromIndex)
|
||||
{
|
||||
// Remove letters from the specified index to the end
|
||||
for (int i = selectedLetters.Count - 1; i >= fromIndex; i--)
|
||||
{
|
||||
selectedLetters[i].SetSelected(false);
|
||||
selectedLetters.RemoveAt(i);
|
||||
}
|
||||
|
||||
// Update UI
|
||||
UpdateSelectedWordText();
|
||||
OnSelectionChanged?.Invoke(selectedLetters);
|
||||
}
|
||||
|
||||
private void UpdateMousePosition()
|
||||
{
|
||||
Vector2 screenPosition = Vector2.zero;
|
||||
|
||||
// Check for touch input first (mobile devices)
|
||||
if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)
|
||||
{
|
||||
screenPosition = Touchscreen.current.primaryTouch.position.ReadValue();
|
||||
}
|
||||
// Check virtual mouse input
|
||||
else if (virtualMouseInput != null && virtualMouseInput.virtualMouse != null && virtualMouseInput.virtualMouse.leftButton.isPressed)
|
||||
{
|
||||
screenPosition = virtualMouseInput.virtualMouse.position.ReadValue();
|
||||
}
|
||||
// Fallback to regular mouse input
|
||||
else if (Mouse.current != null)
|
||||
{
|
||||
screenPosition = Mouse.current.position.ReadValue();
|
||||
}
|
||||
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
parentLetters.GetComponent<RectTransform>(),
|
||||
screenPosition,
|
||||
Camera.main,
|
||||
out currentMousePosition
|
||||
);
|
||||
}
|
||||
|
||||
private bool IsAnyInputActive()
|
||||
{
|
||||
// Check touch input
|
||||
if (Touchscreen.current != null && Touchscreen.current.primaryTouch.press.isPressed)
|
||||
return true;
|
||||
|
||||
// Check virtual mouse
|
||||
if (virtualMouseInput != null && virtualMouseInput.virtualMouse != null && virtualMouseInput.virtualMouse.leftButton.isPressed)
|
||||
return true;
|
||||
|
||||
// Check regular mouse
|
||||
if (Mouse.current != null && Mouse.current.leftButton.isPressed)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnLevelLoaded(Level level)
|
||||
{
|
||||
if (lineRenderer != null)
|
||||
{
|
||||
lineRenderer.color = level.colorsTile.faceColor;
|
||||
}
|
||||
|
||||
layout.GetComponent<Image>().color = level.colorsTile.faceColor;
|
||||
|
||||
// Clean up previous level
|
||||
CleanupLevel();
|
||||
|
||||
var letters = level.GetLetters(gameManager.language);
|
||||
int letterCount = letters.Length;
|
||||
var letterSize = 132 - Mathf.Max(0, letterCount - 6) * 10;
|
||||
|
||||
for (int i = 0; i < letterCount; i++)
|
||||
{
|
||||
// Calculate the angle for this letter (in radians)
|
||||
// Start from the top (90 degrees or π/2) and go clockwise
|
||||
float angle = ((float)i / letterCount) * 2 * Mathf.PI - Mathf.PI/2;
|
||||
|
||||
// Calculate position on the circle
|
||||
float x = circleCenter.x + radius * Mathf.Cos(angle);
|
||||
float y = circleCenter.y + radius * Mathf.Sin(angle);
|
||||
|
||||
// Instantiate button
|
||||
var button = Instantiate(letterButtonPrefab, parentLetters);
|
||||
button.SetColor(level.colorsTile.faceColor);
|
||||
RectTransform rectTransform = button.GetComponent<RectTransform>();
|
||||
|
||||
// Set position
|
||||
rectTransform.anchoredPosition = new Vector2(x, y);
|
||||
|
||||
// Set text
|
||||
button.SetText(letters[i].ToString());
|
||||
button.letterText.fontSize = letterSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the current level state, including selections and UI elements
|
||||
/// </summary>
|
||||
public void CleanupLevel()
|
||||
{
|
||||
// Clear any active selection
|
||||
ClearSelection();
|
||||
|
||||
// Clear any existing letters
|
||||
ClearExistingLetters();
|
||||
|
||||
// Reset UI elements
|
||||
if (selectedWordText != null)
|
||||
{
|
||||
selectedWordText.text = "";
|
||||
}
|
||||
|
||||
// Reset background
|
||||
SetBackgroundAlpha(0f);
|
||||
|
||||
// Reset selection state
|
||||
isSelecting = false;
|
||||
selectedLetters.Clear();
|
||||
|
||||
// Reset game won state and re-enable panel
|
||||
isGameWon = false;
|
||||
SetPanelBlockRaycast(true);
|
||||
}
|
||||
|
||||
private void ClearExistingLetters()
|
||||
{
|
||||
// Find and destroy all existing letter buttons under parentLetters
|
||||
if (parentLetters != null)
|
||||
{
|
||||
LetterButton[] existingButtons = parentLetters.GetComponentsInChildren<LetterButton>();
|
||||
foreach (LetterButton button in existingButtons)
|
||||
{
|
||||
Destroy(button.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StartSelection(LetterButton button)
|
||||
{
|
||||
// Clear any previous selection
|
||||
ClearSelection();
|
||||
|
||||
// Start a new selection
|
||||
isSelecting = true;
|
||||
selectedLetters.Add(button);
|
||||
soundBase.PlayIncremental(selectedLetters.Count);
|
||||
|
||||
// Initialize mouse position
|
||||
UpdateMousePosition();
|
||||
|
||||
// Fade out rearrange button during selection
|
||||
if (shuffleButton != null)
|
||||
{
|
||||
shuffleButton.GetComponent<CanvasGroup>().DOFade(0.0f, 0.3f);
|
||||
}
|
||||
|
||||
// Make background visible when selection starts
|
||||
SetBackgroundAlpha(1f);
|
||||
|
||||
// Start drawing the line
|
||||
UpdateLineRenderer();
|
||||
|
||||
// Update the selected word text
|
||||
UpdateSelectedWordText();
|
||||
|
||||
// Notify listeners about the selection change
|
||||
OnSelectionChanged?.Invoke(selectedLetters);
|
||||
}
|
||||
|
||||
public void AddToSelection(LetterButton button)
|
||||
{
|
||||
if (!isSelecting) return;
|
||||
|
||||
// Don't add if it's already the last selected letter
|
||||
if (selectedLetters.Count > 0 && selectedLetters[selectedLetters.Count - 1] == button)
|
||||
return;
|
||||
|
||||
// Check if this letter is already selected elsewhere in the chain
|
||||
if (selectedLetters.Contains(button))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
soundBase.PlayIncremental(selectedLetters.Count);
|
||||
// Add this letter to our selection
|
||||
selectedLetters.Add(button);
|
||||
// Update the line to include the new letter
|
||||
UpdateLineRenderer();
|
||||
// Update the selected word text
|
||||
UpdateSelectedWordText();
|
||||
// Notify listeners about the selection change
|
||||
OnSelectionChanged?.Invoke(selectedLetters);
|
||||
}
|
||||
|
||||
private void UpdateLineRenderer()
|
||||
{
|
||||
if (lineRenderer == null) return;
|
||||
|
||||
// Create array of points for the line renderer
|
||||
// Add one extra point for the current mouse position when selecting
|
||||
int pointCount = selectedLetters.Count + (isSelecting ? 1 : 0);
|
||||
Vector2[] points = new Vector2[pointCount];
|
||||
|
||||
// Update each selected letter position
|
||||
for (int i = 0; i < selectedLetters.Count; i++)
|
||||
{
|
||||
RectTransform letterRect = selectedLetters[i].GetComponent<RectTransform>();
|
||||
// Use anchoredPosition since that's what we set in OnLevelLoaded
|
||||
points[i] = letterRect.anchoredPosition;
|
||||
}
|
||||
|
||||
// Add current mouse position as the last point if we're actively selecting
|
||||
if (isSelecting && selectedLetters.Count > 0)
|
||||
{
|
||||
points[points.Length - 1] = currentMousePosition;
|
||||
}
|
||||
|
||||
lineRenderer.points = points;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void EndSelection()
|
||||
{
|
||||
if (!isSelecting) return;
|
||||
|
||||
isSelecting = false;
|
||||
|
||||
// Process the word that was formed
|
||||
if (selectedLetters.Count > 0)
|
||||
{
|
||||
string word = GetSelectedWord();
|
||||
// Notify listeners that a word selection is completed
|
||||
OnSelectionCompleted?.Invoke(word);
|
||||
|
||||
// Here you would check if the word is valid and handle scoring
|
||||
// For now, just clear selection after a short delay
|
||||
Invoke("ClearSelection", 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSelectedWord()
|
||||
{
|
||||
string word = "";
|
||||
foreach (var letter in selectedLetters)
|
||||
{
|
||||
word += letter.GetLetter();
|
||||
}
|
||||
return word;
|
||||
}
|
||||
|
||||
// Update the selected word display using character prefabs
|
||||
private void UpdateSelectedWordText()
|
||||
{
|
||||
if (selectedWordText != null)
|
||||
{
|
||||
selectedWordText.color = new Color(selectedWordText.color.r, selectedWordText.color.g, selectedWordText.color.b, 1f);
|
||||
selectedWordText.text = GetSelectedWord();
|
||||
UpdateHorizontalLayout(layout);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
// Clear all visual selection indicators
|
||||
foreach (var letter in selectedLetters)
|
||||
{
|
||||
letter.SetSelected(false);
|
||||
}
|
||||
|
||||
selectedLetters.Clear();
|
||||
isSelecting = false;
|
||||
|
||||
// Fade in rearrange button when selection ends
|
||||
if (shuffleButton != null)
|
||||
{
|
||||
shuffleButton.GetComponent<CanvasGroup>().DOFade(1f, 0.3f);
|
||||
}
|
||||
|
||||
// Clear the line renderer
|
||||
if (lineRenderer != null)
|
||||
{
|
||||
lineRenderer.points = new Vector2[0];
|
||||
}
|
||||
|
||||
if (selectedWordText != null)
|
||||
{
|
||||
selectedWordText.text = "";
|
||||
}
|
||||
// Make background invisible when selection is cleared
|
||||
SetBackgroundAlpha(0f);
|
||||
}
|
||||
|
||||
// Updated method that gets character positions and delegates validation to FieldManager
|
||||
private void ValidateWordWithModel(string word)
|
||||
{
|
||||
if (string.IsNullOrEmpty(word)) return;
|
||||
|
||||
// Get the positions from the actual text characters
|
||||
List<Vector3> letterPositions = new List<Vector3>();
|
||||
|
||||
if (selectedWordText != null && !string.IsNullOrEmpty(selectedWordText.text))
|
||||
{
|
||||
// Force text to update
|
||||
selectedWordText.ForceMeshUpdate();
|
||||
|
||||
// Get mesh info which contains character positions
|
||||
TMP_TextInfo textInfo = selectedWordText.textInfo;
|
||||
|
||||
// Go through each character in the text
|
||||
for (int i = 0; i < word.Length && i < textInfo.characterCount; i++)
|
||||
{
|
||||
// Make sure character is visible (not a space or control character)
|
||||
if (!textInfo.characterInfo[i].isVisible) continue;
|
||||
|
||||
// Get the center position of the character in world space
|
||||
Vector3 bottomLeft = selectedWordText.transform.TransformPoint(textInfo.characterInfo[i].bottomLeft);
|
||||
Vector3 topRight = selectedWordText.transform.TransformPoint(textInfo.characterInfo[i].topRight);
|
||||
Vector3 charCenter = (bottomLeft + topRight) / 2f;
|
||||
|
||||
letterPositions.Add(charCenter);
|
||||
}
|
||||
}
|
||||
|
||||
// Delegate the word validation to FieldManager
|
||||
if (fieldManager != null)
|
||||
{
|
||||
// If the word is already open, show the shake animation
|
||||
if (fieldManager.IsWordOpen(word))
|
||||
{
|
||||
ShakeSelectedWordBackground();
|
||||
soundBase.PlayWrong();
|
||||
}
|
||||
|
||||
// If the word is valid and was successfully opened (or was already open),
|
||||
// animate the UI elements
|
||||
if (fieldManager.ValidateWord(word, letterPositions))
|
||||
{
|
||||
SetPanelBlockRaycast(false);
|
||||
AnimateAlphaDown(backgroundSelectedWord);
|
||||
EventManager.GetEvent<string>(EGameEvent.WordOpened).Invoke(word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGameWon()
|
||||
{
|
||||
isGameWon = true;
|
||||
SetPanelBlockRaycast(false);
|
||||
}
|
||||
|
||||
private void ShakeSelectedWordBackground()
|
||||
{
|
||||
if (backgroundSelectedWord == null) return;
|
||||
|
||||
RectTransform rectTransform = backgroundSelectedWord.GetComponent<RectTransform>();
|
||||
if (rectTransform == null) return;
|
||||
|
||||
// Store original position
|
||||
Vector2 originalPosition = rectTransform.anchoredPosition;
|
||||
|
||||
// Create shake sequence
|
||||
Sequence shakeSequence = DOTween.Sequence();
|
||||
|
||||
// Create quick, small shakes (offset from original position)
|
||||
float shakeAmount = 10f;
|
||||
float shakeDuration = 0.08f;
|
||||
int shakeCount = 3;
|
||||
|
||||
for (int i = 0; i < shakeCount; i++)
|
||||
{
|
||||
// Alternate directions: right, left, right
|
||||
float xOffset = (i % 2 == 0) ? shakeAmount : -shakeAmount;
|
||||
|
||||
shakeSequence.Append(rectTransform.DOAnchorPos(
|
||||
new Vector2(originalPosition.x + xOffset, originalPosition.y),
|
||||
shakeDuration).SetEase(Ease.OutQuad));
|
||||
}
|
||||
|
||||
shakeSequence.Append(rectTransform.DOAnchorPos(originalPosition, shakeDuration));
|
||||
}
|
||||
|
||||
private void AnimateAlphaDown(Image image)
|
||||
{
|
||||
image.DOFade(0f, 0.3f);
|
||||
selectedWordText.DOFade(0f, 0.3f);
|
||||
DOVirtual.DelayedCall(1f, () => {
|
||||
if (!isGameWon)
|
||||
SetPanelBlockRaycast(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void SetPanelBlockRaycast(bool blockRaycast)
|
||||
{
|
||||
if (panelCanvasGroup != null)
|
||||
{
|
||||
panelCanvasGroup.blocksRaycasts = blockRaycast;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to set background alpha
|
||||
private void SetBackgroundAlpha(float alpha)
|
||||
{
|
||||
if (backgroundSelectedWord != null)
|
||||
{
|
||||
Color color = backgroundSelectedWord.color;
|
||||
color.a = alpha;
|
||||
backgroundSelectedWord.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceUpdateLayout(RectTransform layoutRectTransform)
|
||||
{
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(layoutRectTransform);
|
||||
}
|
||||
|
||||
private void UpdateHorizontalLayout(HorizontalLayoutGroup layout)
|
||||
{
|
||||
ForceUpdateLayout(layout.GetComponent<RectTransform>());
|
||||
}
|
||||
|
||||
public void RearrangeRandomLetters()
|
||||
{
|
||||
if (parentLetters == null) return;
|
||||
|
||||
// Get all letter buttons
|
||||
var letterButtons = parentLetters.GetComponentsInChildren<LetterButton>();
|
||||
if (letterButtons.Length < 2) return;
|
||||
|
||||
int swapCount = letterButtons.Length;
|
||||
var lettersToSwap = letterButtons;
|
||||
|
||||
// Create a list of positions and shuffle them
|
||||
var positions = lettersToSwap.Select(btn => btn.GetComponent<RectTransform>().anchoredPosition).ToList();
|
||||
|
||||
// Shuffle positions using Fisher-Yates algorithm
|
||||
for (int i = positions.Count - 1; i > 0; i--)
|
||||
{
|
||||
int randomIndex = UnityEngine.Random.Range(0, i + 1);
|
||||
Vector2 temp = positions[i];
|
||||
positions[i] = positions[randomIndex];
|
||||
positions[randomIndex] = temp;
|
||||
}
|
||||
|
||||
// Create a single sequence for all animations
|
||||
var sequence = DOTween.Sequence();
|
||||
|
||||
// Add all animations to the sequence simultaneously using a single Join group
|
||||
var joinGroup = sequence.Join(DOTween.Sequence());
|
||||
for (int i = 0; i < lettersToSwap.Length; i++)
|
||||
{
|
||||
joinGroup.Join(
|
||||
lettersToSwap[i].GetComponent<RectTransform>()
|
||||
.DOAnchorPos(positions[i], swapAnimationDuration)
|
||||
.SetEase(swapAnimationEase)
|
||||
);
|
||||
}
|
||||
|
||||
sequence.Play();
|
||||
}
|
||||
|
||||
private List<LetterButton> GetRandomLetters(LetterButton[] allLetters, int count)
|
||||
{
|
||||
List<LetterButton> randomLetters = new List<LetterButton>();
|
||||
List<int> availableIndices = new List<int>();
|
||||
|
||||
// Initialize available indices
|
||||
for (int i = 0; i < allLetters.Length; i++)
|
||||
{
|
||||
availableIndices.Add(i);
|
||||
}
|
||||
|
||||
// Pick random letters
|
||||
for (int i = 0; i < count && availableIndices.Count > 0; i++)
|
||||
{
|
||||
int randomIndex = UnityEngine.Random.Range(0, availableIndices.Count);
|
||||
int selectedIndex = availableIndices[randomIndex];
|
||||
randomLetters.Add(allLetters[selectedIndex]);
|
||||
availableIndices.RemoveAt(randomIndex);
|
||||
}
|
||||
|
||||
return randomLetters;
|
||||
}
|
||||
|
||||
public List<LetterButton> GetLetters(string wordForTutorial)
|
||||
{
|
||||
var letters = parentLetters.GetComponentsInChildren<LetterButton>();
|
||||
List<LetterButton> letterButtons = new List<LetterButton>();
|
||||
List<LetterButton> usedLetters = new List<LetterButton>();
|
||||
|
||||
for (int i = 0; i < wordForTutorial.Length; i++)
|
||||
{
|
||||
var letter = wordForTutorial[i];
|
||||
var availableLetter = letters.FirstOrDefault(l =>
|
||||
l.GetLetter().ToLower() == letter.ToString().ToLower() &&
|
||||
!usedLetters.Contains(l));
|
||||
|
||||
if (availableLetter != null)
|
||||
{
|
||||
letterButtons.Add(availableLetter);
|
||||
usedLetters.Add(availableLetter);
|
||||
}
|
||||
else
|
||||
{
|
||||
letterButtons.Add(null);
|
||||
}
|
||||
}
|
||||
return letterButtons;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
canvasGroup.DOFade(0f, 0.3f);
|
||||
}
|
||||
|
||||
public void InstantHide()
|
||||
{
|
||||
canvasGroup.alpha = 0f;
|
||||
}
|
||||
|
||||
public void HideForWin()
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
canvasGroup.DOFade(1f, 0.3f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually set the VirtualMouseInput reference for controller support
|
||||
/// </summary>
|
||||
/// <param name="virtualMouse">The VirtualMouseInput component to use</param>
|
||||
public void SetVirtualMouseInput(VirtualMouseInput virtualMouse)
|
||||
{
|
||||
virtualMouseInput = virtualMouse;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user