How to check if the area is filled or not?
so, I am basically making a puzzle game where a big shape(in black) is given and the player needs to fill that shape/shadow with different smaller sized shapes. For 1st level, I made it so only a specific shape can be placed at the positions, but now for level 2, I need to make it so that any shape can be placed anywhere and it's correct as long as the whole black shape is covered.
Level 1: So as you can see I am showing a reference image on the left side which the player needs to copy.
for Level 2: For Level 2 I need something like this where the user can put any shape anywhere on the black, and the condition for correct is that it needs to cover the whole area and should not go out of the black.
Currently, I have this code for my drag and drop script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class Tess1DragDrop : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IEndDragHandler, IDragHandler {
[SerializeField] private Canvas canvas;
private RectTransform rectTransform;
public Vector3 startPos;
private CanvasGroup canvasGroup;
private GameObject onShadow;
private GameObject onPanel;
Camera cam;
private Vector3 CurrentPos;
public bool atPlace;
private void Awake() {
onShadow = GameObject.FindGameObjectWithTag("onShadow");
onPanel = GameObject.FindGameObjectWithTag("onPanel");
rectTransform = GetComponent<RectTransform>();
canvasGroup = GetComponent<CanvasGroup>();
startPos= rectTransform.anchoredPosition;
cam = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
}
public void Update(){
}
public void OnBeginDrag(PointerEventData eventData) {
// Debug.Log("OnBeginDrag");
eventData.pointerDrag.transform.SetParent(onShadow.transform);
canvasGroup.alpha = .6f;
canvasGroup.blocksRaycasts = false;
this.tag = "Untagged";
}
public void OnDrag(PointerEventData eventData) {
//Debug.Log("OnDrag");
rectTransform.anchoredPosition += eventData.delta / canvas.scaleFactor;
}
public void OnEndDrag(PointerEventData eventData) {
// Debug.Log("OnEndDrag");
if(eventData.pointerDrag.tag!="Panel"){
CurrentPos = cam.WorldToViewportPoint(transform.position);
canvasGroup.alpha = 1f;
canvasGroup.blocksRaycasts = true;
if(rectTransform.anchoredPosition.y < -125f){
eventData.pointerDrag.transform.SetParent(onPanel.transform);
rectTransform.anchoredPosition = startPos;
}
if(rectTransform.anchoredPosition.y > -125f){
//eventData.pointerDrag.transform.SetParent(onShadow.transform);
StartCoroutine(moveBack());
}
if(CurrentPos.x > 0.9f){
eventData.pointerDrag.transform.SetParent(onPanel.transform);
rectTransform.anchoredPosition = startPos;
}
else if(CurrentPos.x < 0.1f){
eventData.pointerDrag.transform.SetParent(onPanel.transform);
rectTransform.anchoredPosition = startPos;
}
else if(CurrentPos.y > 1f){
eventData.pointerDrag.transform.SetParent(onPanel.transform);
rectTransform.anchoredPosition = startPos;
}}
}
IEnumerator moveBack(){
yield return new WaitForSeconds(0.01f);
if(gameObject.tag != "atPlace"){
gameObject.transform.SetParent(onPanel.transform);
gameObject.GetComponent<RectTransform>().anchoredPosition = startPos;
gameObject.tag = "Untagged";
gameObject.GetComponent<Image>().raycastTarget = true;
}
}
public void OnPointerDown(PointerEventData eventData) { //imp method
// Debug.Log("OnPointerDown");
}
}
and for the item slot script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class Tess1ItemSlot : MonoBehaviour, IDropHandler {
private GameObject onShadow;
private GameObject onPanel;
//public bool atPlace;
public void Start(){
onShadow = GameObject.FindGameObjectWithTag("onShadow");
onPanel = GameObject.FindGameObjectWithTag("onPanel");
// atPlace = false;
}
public void OnDrop(PointerEventData eventData) {
// Debug.Log("OnDrop");
if(eventData.pointerDrag.tag!="Panel"){
if (eventData.pointerDrag != null && eventData.pointerDrag.GetComponent<Image>().sprite == gameObject.GetComponent<Image>().sprite) {
eventData.pointerDrag.transform.SetParent(onShadow.transform);
eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = GetComponent<RectTransform>().anchoredPosition;
eventData.pointerDrag.tag = "atPlace";
eventData.pointerDrag.GetComponent<Image>().raycastTarget = false;
// atPlace = true;
}
if(eventData.pointerDrag.GetComponent<Image>().sprite != gameObject.GetComponent<Image>().sprite){
// Debug.Log("NotSame");
eventData.pointerDrag.transform.SetParent(onPanel.transform);
eventData.pointerDrag.GetComponent<RectTransform>().anchoredPosition = eventData.pointerDrag.GetComponent<Tess1DragDrop>().startPos;
eventData.pointerDrag.tag = "Untagged";
eventData.pointerDrag.GetComponent<Image>().raycastTarget = true;
// atPlace = false;
}
}
}
}
Just a note on the answer I posted: the GridLocation class I outlined is not a monobehavior. Your Tess1ItemSlot $$anonymous$$onoBehavior somewhat to the function of the of that class.
Answer by streeetwalker · Mar 17, 2020 at 11:20 AM
Tough problem! I don't see it in your code, but it looks like you are coding yourself into a corner and would think you want to generalize the problem.
If you don't do this, you're going to have to hand code every game level you create. If you do this, you can easily create an infinite number of different levels with much less effort
I've done quite a bit of hex-grid game coding, so all of the following is doable and not that difficult.
It looks like your game area can be described in a triangular grid system - because it looks like all the shapes can be broken down in to isosceles triangles. So you will need to create a grid system. The centers of each triangle in the grid will be the drop locations.
Then you need an array/list of data structures or class objects that hold some information about each triangle in the grid. I'm talking off the top of my head here, so this is spitballing you a solution (seems like your working in 2D) so in my pseudo code:
class GridLocation // will need to create a constructor with these memebers:
Vector2 center_positon // x y pos to reference back to the grid
bool isfilled = false
GameObject filledObject;
// could also include a variable for games such as the sh
// where specific positions only accept a correct shape
// when you create a new game, you'll need to initialize the screen grid shape, and then the array and GridLocation objects
// next link the array to the grid using the GridLocation class
for each screen gird row and column in a loop
array[ i, j ] = new GridLocation( gridlocation_x_y, false, null );
One inescapable issue here is you have to be be able to map the actual grid x and y coordinates to array indices. That way when the user drags a shape on to the game area, you can snap it to the grid screen location and use the snapped x y coordinates to update the correct element in array. This is not that difficult to do, but a triangular grid-to-array mapping is not going to be as simple as if the grid were made of square elements.
When the player drags a shape to the screen and drops it on the grid, use mouse x and y to snap to a grid center, and the map the respective array column and row values and then update the array. You'll also check to see if isFilled is already true, then you may not allow the drop.
array[ i, j ].isFilled = true
array[ i, j ].filledObject = the game object dropped
Now it is a simple matter to loop through the array to see if any of the isFilled are true, and if all are true you know your grid is filled!
Another critical issue is if you have drag and drop shapes that composed of two triangles, like your rhombus in the second image you posted. There you'll have to come up with a scheme of filling multiple grid triangles with one shape, and marking isFilled and the filledObject fields of the respective array elements appropriately. You'd also need information about each shape, so probably a "Shape" class that contains how many grid positions it can fill; a link to the shape gameobject; and so on. That is tougher to do but it is not an impossible problem.
Better to rethink and refactor - fix your code now before you get too far down a road that is not scalable!
The code I posted is for level 1 and it works perfectly as I want it to. For level 2 I don't even know how to begin, all I know is that I will have to start from scratch for it. I will try to go through what you mentioned for sure. Thanks, man!
After looking over your code after i posted that reply, I think you are not far off from the idea that I posted. Really you just need your Tess1ItemSlot instances in an array, and then you can loop through and check the "atPlace" variable you've commented out - if that is what I think you intended to use that for.
The thing is to come up with an easy way of doing new levels. I think Tess1ItemSlot already gives you a way of doing that - if I understand what you are doing with that object, which is to arrange the drop locations in the play area?
With the method I suggested, it takes some effort up front to work out a system and from there it gets easier.
So I think you could pretty easily adapt parts of what I suggested and keep the concept you already have to creating new levels.
Probably it would be better if you decouple the parts of the code that are in common between the levels and put that code into a controller script on an empty object.
For example, it would seem drag and drop code is the same across all your levels, Just use your Tess1ItemSlot class to provide the grid drop locations, and the atPlace value. So move the drag and drop code to the manager script, and you'd check collisions/triggers with the mouse location (offset from the center of the drag shape) to see if the player had dragged/dopped over a specific slot.
Looks like a cool game and code you've got going!
Your answer
Follow this Question
Related Questions
Can I use subtractive shapes? 4 Answers
How to drag object in GridLayoutGroup 0 Answers
how do i put gameobjects into the scene 0 Answers
toggling between prefabs 1 Answer
Problem with Destroy function with instantiated Prefabs 1 Answer