๐ŸŽฎ๋”ฅ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ ๋žœ๋ค ๋ฏธ๋กœ ์ƒ์„ฑ & Unity ๋ฏธ๋กœ ํƒˆ์ถœ ๊ฒŒ์ž„

๋‹ˆ๋‚˜...ยท2025๋…„ 3์›” 3์ผ

๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธ

๋ชฉ๋ก ๋ณด๊ธฐ
1/4
  • ์ดํ™”์—ฌ๋Œ€ ํ˜ธํฌ๋งˆ๊ต์–‘๋Œ€ํ•™ ์†Œ์† ๋จธ์‹ ๋Ÿฌ๋‹ ๋™์•„๋ฆฌ <ํ˜ธ๊ตฌ๋งˆํŒŒ์ด>์—์„œ 24-2 ๊ฒจ์šธ ๋ฐฉํ•™ ํ”„๋กœ์ ํŠธ๋กœ ํ˜ผ์ž ์ง„ํ–‰ํ•œ ๊ฒƒ
  • ์•„์นด์ด๋น™ ๋ชฉ์ ์œผ๋กœ velog์— ๊ธฐ๋กํ•ด๋‘”๋‹ค โœ๐Ÿป

1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

๋”ฅ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•˜์—ฌ ๋ฏธ๋กœ ๋งต์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ , Unity์—์„œ ์ด๋ฅผ ์ ์šฉํ•œ ๋ฏธ๋กœ ํƒˆ์ถœ ๊ฒŒ์ž„์„ ๊ฐœ๋ฐœํ–ˆ๋‹ค.
'Unity๋ฅผ ํ™œ์šฉํ•œ ๊ฒŒ์ž„ ๊ฐœ๋ฐœ'๊ณผ 'AI ๊ธฐ๋ฐ˜ ๋ ˆ๋ฒจ ์ž๋™ ์ƒ์„ฑ'์ด๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ๋ชฉํ‘œ๋ฅผ ๊ฐ€์ง€๊ณ  ์ง„ํ–‰ํ–ˆ๋‹ค.

-VAE ๋ชจ๋ธ์„ ์ด์šฉํ•ด ๋ฏธ๋กœ ๋ฐ์ดํ„ฐ ์„ธํŠธ๋ฅผ ํ•™์Šตํ•˜๊ณ , ๋ณ€์ฃผ๋ฅผ ์ƒ์„ฑํ•จ.
-unity์™€ ์—ฐ๋™ํ•˜์—ฌ์„ ์ƒ์„ฑ๋œ ๋งต์„ ๋žœ๋ค์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ  ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์บ๋ฆญํ„ฐ๋ฅผ ์ง์ ‘ ์กฐ์ž‘ํ•˜์—ฌ ํƒˆ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ.

2. ์ž‘์—… ๊ณผ์ •

2.1. Unity์—์„œ ๊ธฐ๋ณธ ๊ตฌ์กฐ ๋งŒ๋“ค๊ธฐ

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ & ํ™˜๊ฒฝ ์„ธํŒ…

  • Unity 2021.3.11f1 ๋ฒ„์ „์œผ๋กœ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  • 2D ํ”„๋กœ์ ํŠธ ์„ค์ • ํ›„, ํ•„์š”ํ•œ ์• ์…‹ ์ถ”๊ฐ€
  • main camera ๋“ฑ ๊ธฐ๋ณธ์ ์ธ Hierarchy ๊ตฌ์„ฑ

ํ”„๋ฆฌํŒน ์ค€๋น„

  • ํ”Œ๋ ˆ์ด์–ด ์บ๋ฆญํ„ฐ๋Š” ์œ ๋‹ˆํ‹ฐ ์• ์…‹ ์Šคํ† ์–ด์—์„œ ๋ฌด๋ฃŒ ์• ์…‹ ๋‹ค์šด๋กœ๋“œํ•ด์„œ ์‚ฌ์šฉํ–ˆ์Œ (https://assetstore.unity.com/packages/2d/characters/bolt-2d-jellyfarm-assets-pack-188722)
  • ๊ทธ ์™ธ ๋ฐ”๋‹ฅ, ๋ฒฝ, ์ถœ๋ฐœ ์ง€์ , ๋„์ฐฉ ์ง€์ ์€ ์ง์ ‘ ๋งŒ๋“ค์—ˆ๋‹ค

ํ”Œ๋ ˆ์ด์–ด ์ด๋™ ๊ตฌํ˜„

  • PlayerMovement.cs ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ -> ๋ฐฉํ–ฅํ‚ค ์ž…๋ ฅ์œผ๋กœ ํ”Œ๋ ˆ์ด์–ด ์ด๋™ ๊ตฌํ˜„
  • ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š” ์นด๋ฉ”๋ผ ์Šคํฌ๋ฆฝํŠธ๋„ ์ถ”๊ฐ€ํ•ด๋ดค์œผ๋‚˜ ๋ถˆํ•„์š”ํ•œ ๊ฒƒ ๊ฐ™์•„์„œ ์‚ญ์ œํ–ˆ์Œ -> ์ „์ฒด ๋งต์ด ๊ฒŒ์ž„ ํ™”๋ฉด์— ๋‚˜์˜ค๋„๋ก ํ•˜๋Š” ๊ฒŒ ๋” ํ”Œ๋ ˆ์ดํ•˜๊ธฐ์— ์ง๊ด€์ ์ด๋ผ๋Š” ํŒ๋‹จ

2.2 ๋ฐ์ดํ„ฐ์…‹ ์ œ์ž‘ & ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ ํ•™์Šต (VAE)

๋ฏธ๋กœ ๋ฐ์ดํ„ฐ์…‹ ์ˆ˜์ž‘์—… ์ œ์ž‘

  • 15x15 ํฌ๊ธฐ์˜ ๋ฏธ๋กœ ๋ฐ์ดํ„ฐ์…‹ 20๊ฐœ ์ œ์ž‘ (0,1,2,3์œผ๋กœ ๊ตฌ์„ฑ)
  • ๊ตฌ๊ธ€ ์Šคํ”„๋ ˆ๋“œ ์‹œํŠธ ์ผœ์„œ ์นธ์—๋‹ค๊ฐ€ ์ง์ ‘ 1, 1, 1, 0, 1 ์ฑ„์›Œ๊ฐ€๋ฉฐ ํ•™์Šต์‹œํ‚ฌ ๋ฐ์ดํ„ฐ ์„ธํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค ์ดํ›„ csv ํŒŒ์ผ๋กœ ๋ณ€ํ™˜ํ–ˆ์Œ

VAE ๋ชจ๋ธ ํ•™์Šต โญ๏ธ

  • ๋น„๊ต์  ์นœ์ˆ™ํ•œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ Google Colab์—์„œ PyTorch ํ™œ์šฉ
  • VAE ๋ชจ๋ธ์„ ์ด์šฉํ•ด ์ƒˆ๋กœ์šด ๋ฏธ๋กœ ๋งต ์ž๋™ ์ƒ์„ฑํ•˜๊ณ , csv ํŒŒ์ผ๋กœ ์ €์žฅํ–ˆ์Œ
  • Unity Project์˜ Maps ํด๋”์— ์ˆ˜์ œ์ž‘ํ•œ ๋งต + ์ž๋™ ์ƒ์„ฑํ•œ ๋งต์„ ์ €์žฅ

์ผ๋‹จ VAE ํ•™์Šต์˜ ๊ธฐ๋ณธ์ ์ธ ๋ผˆ๋Œ€๊ฐ€ ๋˜๋Š” ์ฝ”๋“œ๋Š” ์ด๋Ÿฌํ•˜๋‹ค... (์ž์ž˜ํ•œ ์˜ค๋ฅ˜ ๋•Œ๋ฌธ์— ๋งŽ์€ ์ˆ˜์ •์„ ๊ฑฐ์ณค์Œ ๐Ÿฅฒ)

import os
import pandas as pd
import torch
import numpy as np

def load_data(data_dir, input_dim):
    data = []
    for file in os.listdir(data_dir):
        file_path = os.path.join(data_dir, file)
        array = pd.read_csv(file_path, header=None).values.flatten()

        array[array == 2] = 0
        array[array == 3] = 0

        data.append(array)
    data = np.array(data)
    return torch.tensor(data, dtype=torch.float32)

data_dir = "/content/maze_dataset/maze_dataset"
input_dim = 15 * 15

import torch.nn as nn

class VAE(nn.Module):
    def __init__(self, input_dim, latent_dim):
        super(VAE, self).__init__()
        self.encoder = nn.Sequential(
            nn.Flatten(),
            nn.Linear(input_dim, 256),
            nn.ReLU(),
            nn.Linear(256, latent_dim * 2)
        )
        self.decoder = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.ReLU(),
            nn.Linear(256, input_dim),
            nn.Sigmoid()
        )
        self.input_dim = input_dim
        self.latent_dim = latent_dim

    def reparameterize(self, mu, log_var):
        std = torch.exp(0.5 * log_var)
        eps = torch.randn_like(std)
        return mu + eps * std

    def forward(self, x):
        x = x.view(-1, self.input_dim)
        params = self.encoder(x)
        mu, log_var = params[:, :self.latent_dim], params[:, self.latent_dim:]
        z = self.reparameterize(mu, log_var)
        recon = self.decoder(z)
        return recon, mu, log_var

# ์†์‹ค ํ•จ์ˆ˜ (Reconstruction Loss + KL Divergence)
def loss_function(recon_x, x, mu, log_var):
    recon_loss = nn.BCELoss()(recon_x, x)
    kl_div = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())
    return recon_loss + kl_div

# ํ•™์Šต ํ•จ์ˆ˜
def train_vae(model, data_loader, epochs, optimizer, device):
    model.train()
    for epoch in range(epochs):
        total_loss = 0
        for x in data_loader:
            x = x.to(device)
            optimizer.zero_grad()
            recon, mu, log_var = model(x)
            loss = loss_function(recon, x, mu, log_var)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        print(f"Epoch {epoch+1}, Loss: {total_loss / len(data_loader)}")

from torch.utils.data import DataLoader
import torch.optim as optim

# ๋ฐ์ดํ„ฐ ๋กœ๋“œ
data = load_data(data_dir, input_dim)
data_loader = DataLoader(data, batch_size=32, shuffle=True)

# ๋ชจ๋ธ ์ดˆ๊ธฐํ™”
latent_dim = 10  # ์ž ์žฌ ๊ณต๊ฐ„ ํฌ๊ธฐ
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = VAE(input_dim=input_dim, latent_dim=latent_dim).to(device)
optimizer = optim.Adam(model.parameters(), lr=0.001)

# ํ•™์Šต ์‹คํ–‰
train_vae(model, data_loader, epochs=50, optimizer=optimizer, device=device)

# ์ƒˆ๋กœ์šด ๋งต ์ƒ์„ฑ ํ•จ์ˆ˜
def generate_map(model, device):
    model.eval()
    with torch.no_grad():
        z = torch.randn(1, model.latent_dim).to(device)  # ๋žœ๋ค ์ž ์žฌ ๋ฒกํ„ฐ
        generated = model.decoder(z)
        generated = generated.view(15, 15).cpu().numpy()
        return np.round(generated)  # 0๊ณผ 1๋กœ ๋ณ€ํ™˜

2.3 Unity์—์„œ CSV ํŒŒ์ผ ๋ถˆ๋Ÿฌ์™€ ๋งต ์ƒ์„ฑํ•˜๊ธฐ

MapLoader.cs ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ

  • csv ํŒŒ์ผ์„ ์ฝ๊ณ  ๋ฒฝ/๋ฐ”๋‹ฅ/์ถœ๊ตฌ/์‹œ์ž‘ ์œ„์น˜ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ ์ถ”๊ฐ€
  • 0์ธ ์œ„์น˜์— ๋ฐ”๋‹ฅ, 1์ธ ์œ„์น˜์— ๋ฒฝ, 2์ธ ์œ„์น˜์— ์ถœ๊ตฌ, 3์ธ ์œ„์น˜์— ์‹œ์ž‘ ํƒ€์ผ์ด ์ž๋™ ์ƒ์„ฑ๋˜๋„๋ก ํ•จ

๋žœ๋ค ๋งต ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„

  • Maps ํด๋” ๋‚ด ๋ฏธ๋กœ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋žœ๋ค์œผ๋กœ ์„ ํƒํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ด
  • ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ถœ๊ตฌ์— ๋„๋‹ฌํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋žœ๋ค ๋งต ์ž๋™ ๋กœ๋”ฉ (ExitTile.cs)

๋งต ํฌ๊ธฐ & ์Šค์ผ€์ผ ์กฐ์ •

  • ๋ฒฝ ํฌ๊ธฐ, ํƒ€์ผ ํฌ๊ธฐ ์กฐ์ •ํ•ด์„œ ๋งต์ด ํ™”๋ฉด์— ๊ฝ‰ ์ฐจ์ง€๋งŒ ์ „๋ถ€ ๋ณด์ด๊ฒŒ ์ˆ˜์ •
  • Unity ๋‹ค๋ฃจ๋Š” ๊ฒŒ ์„œํˆด์–ด์„œ ์‹œ๊ฐ„ ์—„์ฒญ ์žก์•„ ๋ฌต์—ˆ๋‹น . . .

2.4 ์ตœ์ข… ํ…Œ์ŠคํŠธ & ๋งˆ๋ฌด๋ฆฌ ์ž‘์—…

Unity์—์„œ ์ „์ฒด ํ…Œ์ŠคํŠธ ์ง„ํ–‰

  • ํ”Œ๋ ˆ์ด์–ด ์ด๋™ + ๋งต ์ƒ์„ฑ + ๋žœ๋ค ๋งต ๋กœ๋“œ ๊ธฐ๋Šฅ ์ •์ƒ ๋™์ž‘ ํ™•์ธ
  • ๋ฒ„๊ทธ ์ˆ˜์ • ํ•˜๊ณ  ๋งˆ๋ฌด๋ฆฌ ์ž‘์—…

๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ์˜์ƒ ์ดฌ์˜ & ๋ฐœํ‘œ ์ค€๋น„

  • ๊ฒŒ์ž„ ์‹œ์—ฐ ์˜์ƒ ์ดฌ์˜ & ๋ฐœํ‘œ PPT ์ œ์ž‘
  • ๋ฐœํ‘œ ๋‚ด์šฉ ์ •๋ฆฌ ํ›„ ๋ฐœํ‘œ ์ง„ํ–‰ (๋ถ€์›๋“ค์—๊ฒŒ ๋ฐ˜์‘์ด ์ข‹์•„์„œ ๋ฟŒ๋“ฏํ–ˆ์Œ ๐Ÿ˜†)

3. ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •

1์ฐจ ๋ฌธ์ œ - VAE ๋ชจ๋ธ ํ•™์Šต ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

  • ๋ฐ์ดํ„ฐ์…‹์—์„œ 2์™€ 3 ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜ (0๊ณผ 1๋งŒ ์ฝ๊ณ  ํ•™์Šตํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋ธ์ด๋ผ์„œ 2์™€ 3์ด ์žˆ์œผ๋ฉด ์•„์˜ˆ ๋™์ž‘์ด ๋ถˆ๊ฐ€๋Šฅํ–ˆ์Œ)
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• : ๋ชจ๋ธ ํ•™์Šต ์‹œ 2, 3์„ 0์œผ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ ์ƒ์„ฑ๋œ ๋งต์—์„œ ๋‹ค์‹œ ๋ณต๊ตฌ

2์ฐจ ๋ฌธ์ œ - CSV ํŒŒ์ผ ์ฝ๊ธฐ ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

  • CSV ํŒŒ์ผ์ด float(1.0, 0.0) ํ˜•ํƒœ๋กœ ์ €์žฅ๋˜์–ด Unity์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• โ†’ Python์—์„œ CSV๋ฅผ intํ˜•(์ •์ˆ˜)๋กœ ์ €์žฅํ•˜๋„๋ก ์ˆ˜์ •
  • Unity์—์„œ float โ†’ int ๋ณ€ํ™˜ ์ฝ”๋“œ ์ถ”๊ฐ€

๊ธฐํƒ€ ๋ฌธ์ œ ํ•ด๊ฒฐ

  • ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋ฒฝ์— ๋‹ฟ์œผ๋ฉด ํšŒ์ „ํ•˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ
  • ๋ฒฝ ๋‘๊ป˜ ์กฐ์ • ํ›„ ๋„ˆ๋ฌด ๋‘๊ป๊ฑฐ๋‚˜ ์–‡์ง€ ์•Š๋„๋ก ์ˆ˜์ • (์ด๊ฑฐ ํ•˜๋ฉด์„œ Unity Tilesheet ๊ธฐ๋Šฅ ๋งŽ์ด ๋ฐฐ์›€)
  • ์นด๋ฉ”๋ผ๊ฐ€ ๋งต ๋ฐ–์„ ๋ฒ—์–ด๋‚˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ (๊ฒฐ๊ตญ์€ ์Šค์ผ€์ผ ์กฐ์ •์˜ ๋ฌธ์ œ)


๋ฒจ๋กœ๊ทธ์—” ๋™์˜์ƒ ์ฒจ๋ถ€๊ฐ€ ์•ˆ ๋˜๋Š”๊ตฌ๋‚˜ ใ…Žใ…ใ…Ž

0๊ฐœ์˜ ๋Œ“๊ธ€