inspector에서 string으로 원하는 scene의 이름을 저장한 후 클릭을 하면 해당 씬으로 이동
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class ChangeScene : MonoBehaviour
{
public string sceneName;
private Button btn;
void Start()
{
btn = GetComponent<Button>();
btn.onClick.AddListener(() =>
{
SceneManager.LoadScene(sceneName);
});
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.U2D;
using Random = UnityEngine.Random;
public class Test : MonoBehaviour
{
public Board board;
private void Start()
{
board.CreateBoard();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 1);
RaycastHit2D raycastHit2D = Physics2D.Raycast(ray.origin, ray.direction);
if (raycastHit2D.collider != null)
{
Block block = raycastHit2D.collider.GetComponent<Block>();
Debug.Log($"[{block.row}, {block.col}] ({block.transform.position.x}, {block.transform.position.y}), {block.blockType}");
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class Block : MonoBehaviour
{
public enum BlockType
{
Rabbit, Pig, Rat, Monkey, Cat, Chick, Puppy
}
public BlockType blockType;
public SpriteRenderer spriteRenderer;
public TMP_Text debugText;
public int row;
public int col;
public void Init(BlockType blockType)
{
this.blockType = blockType;
//이미지 변경
ChangeSprite(blockType);
}
public void ChangeSprite(BlockType blockType)
{
Sprite sp =
AtlasManager.instance.blockAtlas.GetSprite(blockType.ToString());
spriteRenderer.sprite = sp;
}
public void SetPosition(Vector2 pos)
{
transform.position = pos;
var index = Position2Index(pos);
row = index.row;
col = index.col;
debugText.text = $"[{index.row},{index.col}]";
}
public static (int row, int col) Position2Index(Vector2 pos)
{
return ((int)pos.y, (int)pos.x);
}
public static (int x, int y) Index2Position(Vector2 index)
{
return ((int)index.x, (int)index.y);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Block))]
public class BlockEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
Block block = target as Block;
if (GUILayout.Button("제거"))
{
Debug.Log($"[{block.row},{block.col}]을 제거 합니다.");
Board.instance.RemoveBlock(block.row, block.col);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Random = UnityEngine.Random;
public class Board : MonoBehaviour
{
public static Board instance; //싱글톤
private Block[,] board; //2차원 배열이 Block들을 관리
public GameObject blockPrefab;
public int totalHeight = 14;
public int height = 7;
public int width = 7;
private void Awake()
{
instance = this;
}
public void CreateBoard()
{
//크기가 7개인 Block타입의 2차원 배열을 만들기
board = new Block[totalHeight, width];
for (int i = 0; i < height; i++) //행
{
for (int j = 0; j < width; j++) //열
{
CreateBlock(i, j);
}
}
PrintBoard();
}
public void CreateBlock(int row, int col)
{
Vector2 pos = new Vector2(col, row);
Block.BlockType blockType = (Block.BlockType)Random.Range(0, 7); //0 ~ 6
GameObject blockGo = Instantiate(blockPrefab, this.transform);
Block block = blockGo.GetComponent<Block>();
block.Init(blockType);
block.SetPosition(pos);
//배열의 요소에 블록 넣기
board[row, col] = block;
}
public void RemoveBlock(int row, int col)
{
Block block = board[row, col];
Destroy(block.gameObject); //게임오브젝트 파괴
//배열의 요소를 비워줌
board[row, col] = null;
}
public void PrintBoard()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < height; i++) //행
{
for (int j = 0; j < width; j++) //열
{
Block block = board[i, j];
string strElement = (block == null) ? "null" : block.blockType.ToString();
// 각 엘리먼트를 고정된 너비로 출력
sb.AppendFormat("[{0},{1}] = {2,-10}", i, j, strElement);
}
sb.AppendLine();
}
Debug.Log(sb.ToString());
}
private int[] arrEmptySpaceCol; //빈공간이 있는 컬럼 배열
public void FindEmptySpaceFromColumn()
{
arrEmptySpaceCol = new int[width]; //배열 초기화
//0 : 행, 1: 열
for (int i = 0; i < width; i++) //열
{
int existBlockCnt = 0;
for (int j = 0; j < totalHeight; j++) //행
{
Block block = board[j, i];
if (block != null) existBlockCnt++;
}
int emptySpace = height - existBlockCnt;
arrEmptySpaceCol[i] = emptySpace;
Debug.Log($"{i} 열의 빈공간은 {emptySpace}개 입니다.");
}
}
public void CreateNewBlocks()
{
// arrEmptySpaceCol가 null이거나 모든 요소가 0이면 빈공간이 없음
if (arrEmptySpaceCol == null || arrEmptySpaceCol.All(x => x == 0))
{
Debug.Log("빈공간이 없습니다.");
}
else
{
for (int i = 0; i < arrEmptySpaceCol.Length; i++)
{
int cnt = arrEmptySpaceCol[i]; //i(col)에 몇개 빈공간이 있는가?
if (cnt > 0)
{
Debug.Log($"{i}열에 {cnt}만큼 블록 생성 필요");
CreateBlockFromColumn(i, cnt);
}
}
}
arrEmptySpaceCol = null;
}
private void CreateBlockFromColumn(int col, int cnt)
{
int startRow = height; //행
for (int i = 0; i < cnt; i++)
{
//[7, col] -> [8, col] -> [9,col]
CreateBlock(startRow, col);
startRow++;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(Board))]
public class BoardEditor : Editor
{
private int currentColIdx = 0;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
Board board = target as Board;
if (GUILayout.Button("배열 요소 출력"))
{
board.PrintBoard(); ;
}
GUILayout.Space(5);
if (GUILayout.Button("각 열에 빈공간 찾기"))
{
board.FindEmptySpaceFromColumn();
}
GUILayout.Space(5);
if (GUILayout.Button("새로운 블록들 생성하기"))
{
board.CreateNewBlocks();
}
GUILayout.Space(5);
if (GUILayout.Button($"현재 행({currentColIdx})에 있는 모든 블록 내려보내기"))
{
Debug.Log($"{currentColIdx}행에 있는 모든 블록 내려 보냅니다.");
currentColIdx++;
if (currentColIdx > 6)
{
currentColIdx = 0;
}
}
GUILayout.Space(5);
if (GUILayout.Button("리셋 행"))
{
currentColIdx = 0;
}
}
}
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class ActiveCard : MonoBehaviour
{
public GameObject cardPrefab; // 인스펙터에서 프리팹을 할당하세요
public TMP_Text valueTextPrefab; // 인스펙터에서 텍스트 프리팹을 할당하세요
public Transform parentTransform; // 인스펙터에서 부모 변환을 할당하세요
void Start()
{
PlayerManager.Instance.LoadPlayerCardData();
PlayerManager.Instance.LoadPlayerCardInfo();
PlayerManager.Instance.LoadPlayerCardInfoListData();
foreach (var playerActiveCardInfo in PlayerManager.Instance.dicPlayerCardInfoList)
{
Debug.Log("버튼이 눌림");
// 프리팹에서 새 카드 오브젝트를 복제합니다.
GameObject newCard = Instantiate(cardPrefab, parentTransform);
// 카드의 자식 오브젝트로서 새로운 TMP_Text를 생성합니다.
TMP_Text valueText = Instantiate(valueTextPrefab, newCard.transform);
// 값을 설정합니다.
valueText.text = playerActiveCardInfo.Value.value.ToString();
Debug.Log(valueText.text);
}
Debug.Log("데이터 개수: " + PlayerManager.Instance.dicPlayerCardInfoList.Count);
}
}
데이터 불러오기
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class ActiveCard : MonoBehaviour
{
public TMP_Text valueTextPrefab;
public TMP_Text cardNamePrefab;
public TMP_Text cardInfoPrefab;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ActiveCardDecBtn : MonoBehaviour
{
public Button btn;
public GameObject ActiveCardInventory;
public GameObject cardPrefab;
public Transform parentTransform;
void Start()
{
btn.onClick.AddListener(() =>
{
ActiveCardInventory.SetActive(true);
});
List<PlayerCardInfo> card = new List<PlayerCardInfo>(PlayerManager.Instance.dicPlayerCardInfo.Values);
for (int i = 0; i < PlayerManager.Instance.dicPlayerCardInfoList.Count; i++)
{
Debug.Log("버튼이 눌림");
// 프리팹에서 새 카드 오브젝트를 생성
GameObject Card = Instantiate(cardPrefab, parentTransform);
ActiveCard newCard = Card.GetComponent<ActiveCard>();
// 값을 설정
newCard.valueTextPrefab.text = card[i].value.ToString();
newCard.cardNamePrefab.text = card[i].card_name;
newCard.cardInfoPrefab.text = PlayerManager.Instance.dicPlayerCardDatas[card[i].card_original_id].desc;
}
}
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
[CustomEditor(typeof(PenguinArea))]
public class PenguinAreaEditor : Editor
{
public override void OnInspectorGUI()
{
PenguinArea penguinArea = (PenguinArea)target;
base.OnInspectorGUI();
if (GUILayout.Button("Choose Random Position"))
{
Debug.Log("ChooseRandomPosition");
Vector3 center = penguinArea.transform.position;
float minAngle = -15;
float maxAngle = 15;
float minRadius = 1;
float maxRadius = 2;
Vector3 randPos =
PenguinArea.ChooseRandomPosition(center, minAngle, maxAngle, minRadius, maxRadius);
Debug.Log(randPos);
Debug.DrawLine(center, randPos, Color.red, 10);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PenguinArea : MonoBehaviour
{
[Tooltip("The agent inside the area")]
public PenguinAgent penguinAgent;
[Tooltip("The baby penguin inside the area")]
public GameObject penguinBaby;
[Tooltip("The TextMeshPro text that shows the cumulative reward of the agent")]
public TextMeshPro cumulativeRewardText;
[Tooltip("Prefab of a live fish")]
public Fish fishPrefab;
private List<GameObject> fishList;
//씬에 남아 있는 물고기 수
public int FishRemaining
{
get { return fishList.Count; }
}
private void Start()
{
this.fishList = new List<GameObject>();
ResetArea();
}
private void Update()
{
// Update the cumulative reward text
cumulativeRewardText.text = penguinAgent.GetCumulativeReward().ToString("0.00");
}
public void ResetArea()
{
RemoveAllFish();
PlacePenguin();
PlaceBaby();
SpawnFish(4, .5f);
}
//엄마 펭귄의 이동속도, 회전속도 초기화 및 랜덤위치, 랜덤회전
private void PlacePenguin()
{
Rigidbody rigidbody = penguinAgent.GetComponent<Rigidbody>();
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
penguinAgent.transform.position = ChooseRandomPosition(transform.position, 0f, 360f, 0f, 9f) + Vector3.up * .5f;
penguinAgent.transform.rotation = Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f);
}
private void PlaceBaby()
{
Rigidbody rigidbody = penguinBaby.GetComponent<Rigidbody>();
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
penguinBaby.transform.position = ChooseRandomPosition(transform.position, -45f, 45f, 4f, 9f) + Vector3.up * .5f;
penguinBaby.transform.rotation = Quaternion.Euler(0f, 180f, 0f);
}
//물고기를 먹었다면 리스트에서 제거하고 씬에서 제거한다
public void RemoveSpecificFish(GameObject fishObject)
{
fishList.Remove(fishObject);
Destroy(fishObject);
}
public static Vector3 ChooseRandomPosition(Vector3 center, float minAngle, float maxAngle, float minRadius, float maxRadius)
{
float radius = minRadius;
float angle = minAngle;
if (maxRadius > minRadius)
{
// Pick a random radius
radius = UnityEngine.Random.Range(minRadius, maxRadius);
}
if (maxAngle > minAngle)
{
// Pick a random angle
angle = UnityEngine.Random.Range(minAngle, maxAngle);
}
// Center position + forward vector rotated around the Y axis by "angle" degrees, multiplies by "radius"
return center + Quaternion.Euler(0f, angle, 0f) * Vector3.forward * radius;
}
//count만큼 fish프리팹 인스턴스 생성, 리스트에 넣기, 이동속도 설정하기
private void SpawnFish(int count, float fishSpeed)
{
for (int i = 0; i < count; i++)
{
// Spawn and place the fish
GameObject fishObject = Instantiate<GameObject>(fishPrefab.gameObject);
fishObject.transform.position = ChooseRandomPosition(transform.position, 100f, 260f, 2f, 13f) + Vector3.up * .5f;
fishObject.transform.rotation = Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f);
// Set the fish's parent to this area's transform
fishObject.transform.SetParent(transform);
// Keep track of the fish
fishList.Add(fishObject);
// Set the fish speed
fishObject.GetComponent<Fish>().fishSpeed = fishSpeed;
}
}
private void RemoveAllFish()
{
if (fishList != null)
{
for (int i = 0; i < fishList.Count; i++)
{
if (fishList[i] != null)
{
Destroy(fishList[i]);
}
}
}
fishList = new List<GameObject>();
}
private void OnDrawGizmos()
{
Vector3 center = this.transform.position;
Vector3 dir = Vector3.forward;
float angleRange = 30f;
float radius = 2f;
GizmosExtensions.DrawWireArc(center, dir, angleRange, radius);
}
}
using UnityEngine;
public class GizmosExtensions
{
private GizmosExtensions() { }
public static void DrawWireArc(Vector3 position, Vector3 dir, float anglesRange, float radius, Color color, float maxSteps = 20)
{
Gizmos.color = color;
var srcAngles = GetAnglesFromDir(position, dir);
var initialPos = position;
var posA = initialPos;
var stepAngles = anglesRange / maxSteps;
var angle = srcAngles - anglesRange / 2;
for (var i = 0; i <= maxSteps; i++)
{
var rad = Mathf.Deg2Rad * angle;
var posB = initialPos;
posB += new Vector3(radius * Mathf.Cos(rad), radius * Mathf.Sin(rad), 0);
Gizmos.DrawLine(posA, posB);
angle += stepAngles;
posA = posB;
}
Gizmos.DrawLine(posA, initialPos);
}
/// <summary>
/// Draws a wire arc.
/// </summary>
/// <param name="position"></param>
/// <param name="dir">The direction from which the anglesRange is taken into account</param>
/// <param name="anglesRange">The angle range, in degrees.</param>
/// <param name="radius"></param>
/// <param name="maxSteps">How many steps to use to draw the arc.</param>
public static void DrawWireArc(Vector3 position, Vector3 dir, float anglesRange, float radius, float maxSteps = 20)
{
var srcAngles = GetAnglesFromDir(position, dir);
var initialPos = position;
var posA = initialPos;
var stepAngles = anglesRange / maxSteps;
var angle = srcAngles - anglesRange / 2;
for (var i = 0; i <= maxSteps; i++)
{
var rad = Mathf.Deg2Rad * angle;
var posB = initialPos;
posB += new Vector3(radius * Mathf.Cos(rad), 0, radius * Mathf.Sin(rad));
Gizmos.DrawLine(posA, posB);
angle += stepAngles;
posA = posB;
}
Gizmos.DrawLine(posA, initialPos);
}
static float GetAnglesFromDir(Vector3 position, Vector3 dir)
{
var forwardLimitPos = position + dir;
var srcAngles = Mathf.Rad2Deg * Mathf.Atan2(forwardLimitPos.z - position.z, forwardLimitPos.x - position.x);
return srcAngles;
}
}
using System.Collections;
using System.Collections.Generic;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using UnityEngine;
public class PenguinAgent : Agent
{
[Tooltip("How fast the agent moves forward")]
public float moveSpeed = 5f;
[Tooltip("How fast the agent turns")]
public float turnSpeed = 180f;
[Tooltip("Prefab of the heart that appears when the baby is fed")]
public GameObject heartPrefab;
[Tooltip("Prefab of the regurgitated fish that appears when the baby is fed")]
public GameObject regurgitatedFishPrefab;
private PenguinArea penguinArea;
new private Rigidbody rigidbody;
private GameObject baby;
private bool isFull; // If true, penguin has a full stomach
public override void Initialize()
{
base.Initialize();
penguinArea = GetComponentInParent<PenguinArea>();
baby = penguinArea.penguinBaby;
rigidbody = GetComponent<Rigidbody>();
}
public override void OnActionReceived(ActionBuffers actionBuffers)
{
// Convert the first action to forward movement
float forwardAmount = actionBuffers.DiscreteActions[0];
// Convert the second action to turning left or right
float turnAmount = 0f;
if (actionBuffers.DiscreteActions[1] == 1f)
{
turnAmount = -1f;
}
else if (actionBuffers.DiscreteActions[1] == 2f)
{
turnAmount = 1f;
}
// Apply movement
rigidbody.MovePosition(transform.position + transform.forward * forwardAmount * moveSpeed * Time.fixedDeltaTime);
transform.Rotate(transform.up * turnAmount * turnSpeed * Time.fixedDeltaTime);
// Apply a tiny negative reward every step to encourage action
if (MaxStep > 0) AddReward(-1f / MaxStep);
}
public override void Heuristic(in ActionBuffers actionsOut)
{
int forwardAction = 0;
int turnAction = 0;
if (Input.GetKey(KeyCode.W))
{
// move forward
forwardAction = 1;
}
if (Input.GetKey(KeyCode.A))
{
// turn left
turnAction = 1;
}
else if (Input.GetKey(KeyCode.D))
{
// turn right
turnAction = 2;
}
// Put the actions into the array
actionsOut.DiscreteActions.Array[0] = forwardAction;
actionsOut.DiscreteActions.Array[1] = turnAction;
}
public override void OnEpisodeBegin()
{
isFull = false;
penguinArea.ResetArea();
}
public override void CollectObservations(VectorSensor sensor)
{
// Whether the penguin has eaten a fish (1 float = 1 value)
sensor.AddObservation(isFull);
// Distance to the baby (1 float = 1 value)
sensor.AddObservation(Vector3.Distance(baby.transform.position, transform.position));
// Direction to baby (1 Vector3 = 3 values)
sensor.AddObservation((baby.transform.position - transform.position).normalized);
// Direction penguin is facing (1 Vector3 = 3 values)
sensor.AddObservation(transform.forward);
// 1 + 1 + 3 + 3 = 8 total values
}
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.CompareTag("fish"))
{
// Try to eat the fish
EatFish(collision.gameObject);
}
else if (collision.transform.CompareTag("baby"))
{
// Try to feed the baby
RegurgitateFish();
}
}
private void EatFish(GameObject fishObject)
{
if (isFull) return; // Can't eat another fish while full
isFull = true;
penguinArea.RemoveSpecificFish(fishObject);
AddReward(1f);
}
private void RegurgitateFish()
{
if (!isFull) return; // Nothing to regurgitate
isFull = false;
// Spawn regurgitated fish
GameObject regurgitatedFish = Instantiate<GameObject>(regurgitatedFishPrefab);
regurgitatedFish.transform.parent = transform.parent;
regurgitatedFish.transform.position = baby.transform.position;
Destroy(regurgitatedFish, 4f);
// Spawn heart
GameObject heart = Instantiate<GameObject>(heartPrefab);
heart.transform.parent = transform.parent;
heart.transform.position = baby.transform.position + Vector3.up;
Destroy(heart, 4f);
AddReward(1f);
if (penguinArea.FishRemaining <= 0)
{
EndEpisode();
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fish : MonoBehaviour
{
[Tooltip("The swim speed")]
public float fishSpeed;
private float randomizedSpeed = 0f;
private float nextActionTime = -1f;
private Vector3 targetPosition;
private void FixedUpdate()
{
if (fishSpeed > 0f)
{
Swim();
}
}
private void Swim()
{
// If it's time for the next action, pick a new speed and destination
// Else, swim toward the destination
if (Time.fixedTime >= nextActionTime)
{
// Randomize the speed
randomizedSpeed = fishSpeed * UnityEngine.Random.Range(.5f, 1.5f);
// Pick a random target
targetPosition = PenguinArea.ChooseRandomPosition(transform.parent.position, 100f, 260f, 2f, 13f);
// Rotate toward the target
transform.rotation = Quaternion.LookRotation(targetPosition - transform.position, Vector3.up);
// Calculate the time to get there
float timeToGetThere = Vector3.Distance(transform.position, targetPosition) / randomizedSpeed;
nextActionTime = Time.fixedTime + timeToGetThere;
}
else
{
// Make sure that the fish does not swim past the target
Vector3 moveVector = randomizedSpeed * transform.forward * Time.fixedDeltaTime;
if (moveVector.magnitude <= Vector3.Distance(transform.position, targetPosition))
{
transform.position += moveVector;
}
else
{
transform.position = targetPosition;
nextActionTime = Time.fixedTime;
}
}
}
}