본문 바로가기
IT

[Unity/Zepeto] 특정 물체로부터 주변 물체를 찾아나가는 기능(BFS) 만들기

by 배애앰이 좋아 2023. 8. 23.
반응형

 

불이 퍼져나가는 현상처럼 주변 물체를 찾아 이벤트를 주는 기능을 구현

 

간단한 구현 방식 설명 : 물체들의 거리를 통해 그래프를 생성하고 랜덤 값을 통해 특정 물체가 선택되었을 때, 그래프와 BFS 방법을 통해 주변 물체부터 찾아가는 구현 방식 >> 아래 프로젝트는 위 방식을 이용해 화재 재앙을 구현하였습니다.

 

아래와 같이 스크립트에 그래프로 생성할 객체들을 다 넣어줍니다. ( 추가로 화재 기능을 구현하고자 하면, 불 이펙트를 자식 0번째 객체로 넣어줍니다. )

 

 

아래 스크립트를 만들고 씬 오브젝트에 넣어줍니다.

DisasterStart() 함수를 통해 무작위로 특정 건물을 지정해 불을 발생합니다. 해당 불은 모든 건물을 태울 때까지 멈추지 않습니다.

Reset() 함수를 통해 모든 값을 초기화 후 해당 화재 이벤트를 재 사용할 수 있게 도와줍니다.

수정하여 사용한다면 bfs_shortest_path() 함수를 활용하면 됩니다.

fireDamge 변수를 통해 건물이랑 플레이어가 받는 데미지 값을 조정할 수 있습니다.

 

import { GameObject, Vector3, WaitForSeconds } from 'UnityEngine';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import TS_HouseController from './TS_HouseController';
import TS_House from './TS_House';
import ClientStarter from './ClientStarter';

export default class TS_FireController extends ZepetoScriptBehaviour {

    public isStart : boolean = false; // is this Disaster start?

    private HouseObj : GameObject[] = []; // house obj
    private HouseTS : TS_House[] = []; // house typescript
    private fires : GameObject[] = []; // fire GameObject
    private currentHouseNum : number; // current house num

    private NearDistance : number = 20; // find house in neardistance
    private visited : boolean[]; // is already visited?
    private graph : number[][] = []; // near house num
    private pathLengths : number[]; // current house num

    public fireDamge : number = 1; // fire Damge

    // singleton
    private static Instance : TS_FireController;
    public static GetInstance() : TS_FireController{
        if(!TS_FireController.Instance){
            const targetObj = GameObject.Find("FireController");
            if(targetObj){
                TS_FireController.Instance = targetObj.GetComponent<TS_FireController>();
            }
        }
        return TS_FireController.Instance;
    }

    Start(){
        this.HouseTS = [];
        this.HouseObj = TS_HouseController.GetInstance().HouseObj;

        this.currentHouseNum = this.HouseObj.length;
        this.visited = new Array(this.currentHouseNum).fill(false);
        this.pathLengths = new Array(this.currentHouseNum).fill(0);

        // create bfs graph
        for(let i = 0; i< this.currentHouseNum; i++){
            this.HouseTS.push(this.HouseObj[i].GetComponent<TS_House>());
            this.fires.push(this.HouseObj[i].transform.GetChild(0).gameObject);
            let array = [];
            for(let j = 0; j < this.currentHouseNum; j++){
                if(i != j){
                    let obj1 = this.HouseObj[i].transform.GetChild(0).gameObject.transform.position;
                    let obj2 = this.HouseObj[j].transform.GetChild(0).gameObject.transform.position;
                    if(Vector3.Distance(obj1, obj2) < this.NearDistance){
                        array.push(j);
                    }
                }
            }
            this.graph.push(array);
            this.fires[i].SetActive(false);
        }
    }

    DisasterStart()
    {
        this.isStart = true;
        // select start house num
        let Num = this.randomNum(0,this.currentHouseNum-1);
        // if player is host, send house num data
        ClientStarter.GetInstance().SendFireNum(Num);
    }

    StartFire(Num : number)
    {
        // get house num data to sever
        this.StartCoroutine(this.bfs_shortest_path(this.graph, Num, this.visited, this.pathLengths));
    }

    *bfs_shortest_path(graph: number[][], v: number, visited, pathLengths: number[]) {
        // start finding near house and event

        // first house
        const queue = [v];
        visited[v] = true;
        this.fires[v].SetActive(true);
        this.HouseTS[v].SetFire(this.fireDamge);
    
        // next near house
        while (queue.length) {
            const v = queue.shift();
            let array = [];
            for (const i of graph[v]) {
                if (!visited[i]) {
                    array.push(i);
                    queue.push(i);
                    visited[i] = true;
                    pathLengths[i] = pathLengths[v] + 1;
                }
            }
            // near house event
            if(array.length > 0)
            {
                //console.log(`fire : ${array}`);
                yield new WaitForSeconds(5);
                for(let j =0; j<array.length; j++){
                    this.fires[array[j]].SetActive(true);
                    this.HouseTS[array[j]].SetFire(this.fireDamge);
                }
            }
        }
    }

    // pick random num
    randomNum(min : number , max : number)
    {
        var randNum = Math.floor(Math.random()*(max-min+1)) + min;
        return randNum;
    }

    Reset()
    {
        this.isStart = false;
        this.visited = new Array(this.currentHouseNum).fill(false);
        this.pathLengths = new Array(this.currentHouseNum).fill(0);
        for(let j = 0; j < this.currentHouseNum; j++){
            this.fires[j].SetActive(false);
        }
    }

}

 

관련영상 :

 

 

반응형

댓글