250325

lililllilillll·2025년 3월 25일

개발 일지

목록 보기
121/350

✅ What I did today


  • Project BCA


🎮 Project BCA


자고 일어나보니 과연 새로 추가할 기믹이 재밌을지 의문.
차라리 가챠 기믹이 더 나아보였음.
일단 만들고 테스트해보기.

AI Random piece removing

Plan

2페이즈는 한 판만. 둘 다 기회 3번. 레이팅은 그대로.

기획

  • 원하는 것 : 통제된 우연성을 가진 페널티
  • 예고된 턴에 플레이어의 기물을 삭제한 후에 다음 움직임을 실행한다.
  • 삭제당할 기물은 랜덤으로 정해진다. 만약 강제 삭제 당하기 전에 기물이 죽었다면, 목표가 다른 기물로 바뀐다.
  • 예고된 턴은 20턴 간격 (1턴 = 흑백 1번씩) 에 한 번씩, 10을 기준으로 정규분포 랜덤으로. < 사양 변경함
  • 로봇팔이 움직이는 사이에 계산하면 대기 시간이 줄어들 수도 있겠지만 유저 사양이 구리면 그 사이에 턴이 넘어가 게임이 꼬일수도. < 해결됨

기존 로직 흐름 정리

  • 체스 기물을 움직이는 클릭을 한다
  • InputHandler.cs에서 moveto()를 호출하고, 그 안에서 piececommand를 queue에 넣는다
  • 이동이 다 끝나면 Gamemanager.cs의 EndTurn()이 호출된다.
  • EndTurn()에서 ChangeState()를 호출하고, ChangeState()에선 바뀔 상태의 EnterState()를 호출한다.
  • 플레이어에서 AI로 턴이 넘어갈 때, AITurnState()의 EnterState()가 호출되면 AIManager.cs의 SendPlayerMoveToStockfish()를 호출한다.
  • 이때 정보가 간 후 Stockfish에서 연산을 마치면 응답이 돌아오고, SendAIMoveToGameManager()가 호출되어 정보를 반영하고 움직임을 예약한다.

슈도 코드

  • (1. 게임이 시작될 때, 2. 목표로 하던 기물이 게임 중 죽었을 때, 3. 예고한 턴에 목표 기물을 제거한 후)에 AIManager.cs의 UpdateNextTarget()으로 목표 기물을 업데이트해야 함
  • 목표 기물을 업데이트했다면 TerminalText.cs의 originalText에 해당 정보를 추가한다.
  • SendPlayerMoveToStockfish()를 호출하기 전에, phase가 2라면 AIManager.cs의 CheckAIDelete()를 호출하여 만약 목표 턴이라면 보드 배열에서 해당 기물을 삭제하고 삭제 움직임을 예약한다.

Prevent unwanted turn transition

문제

  • 기물 이동 애니메이션이 순서대로 이뤄지도록 하는 pieceCommandManager는, 입력받았던 명령이 모두 끝나면 상대에게 턴을 넘김.
  • 약속된 턴이 되어 플레이어의 기물을 삭제한 후, AI의 계산이 아직 끝나지 않았더라도 플레이어에게 턴이 넘어가버려 게임이 꼬일 수 있음.
  • 이를 해결하기 위해 ICommand에 willEndTurn 플래그를 추가하여 해당 동작이 끝났을 때 상대에게 무조건 턴을 넘기지는 않게 수정하려 함.

기존 로직 흐름 정리

  • 기물을 움직이거나 삭제할 때 EnQueueRoboticArmMove()등을 통해 PieceCommandManager.cs에 동작을 예약.
  • 등록될 때 ExecuteNextCommand()를 실행하고, 이미 실행중인 애니메이션이 있다면 그대로 return됨.
  • 무시된 애니메이션은 먼저 실행되던 애니메이션이 CompleteCommand()를 호출하면 또 다시 ExecuteNextCommand()를 호출하며 큐에서 다음 동작을 꺼내어 실행함.
  • 더 이상 꺼낼 동작이 없다면 그대로 턴을 넘김.
  • 동작을 실행할 때, RoboticArmCommand 클래스에 있는 Execute()를 호출하게 됨. 이는 각각 roboticArm, InvisibleArm의 코드를 호출함.

해결

  • CompleteCommand()에서 willEndTurn 정보를 최종적으로 반환해야 함
  • 생성자 > Execute > 로봇팔, 투명손 > CompleteCommand > ExecuteNextCommand 순으로 매개변수를 계속 돌리는 해결책이 떠올랐지만 너무 복잡.
  • 기존 moveQueue.Dequeue().Execute()에 작업 추가. Dequeue()한 ICommand에서 willEndTurn을 가져와 기록해두고
  • 동작이 모두 끝났을 때 willEndTurn이라면 EndTurn() 호출

변경된 코드 일부

    public bool DestroyPieceAt(int x, int z, bool isTurnEnd = true)
    {
        if (pieces[x, z] == null) { return false; }

        bool isWhiteNow = gameManager.whiteTurn; // 백이 플레이어

        if (!isTurnEnd) // 버튼을 누른 거라면 턴을 종료하지 않고 무덤에만 보내기
        {
            if (isWhiteNow) pieceCommandManager.EnQueuePlayerMove(pieces[x, z], piece_grave.GetPlayerGravePos(pieces[x, z]), 0.25f, false);
            else pieceCommandManager.EnQueueRoboticArmMove(pieces[x, z], piece_grave.GetAIGravePos(pieces[x, z]), 0.4f, false);
        }
        else
        {
            if (isWhiteNow) pieceCommandManager.EnQueuePlayerMove(pieces[x, z], piece_grave.GetPlayerGravePos(pieces[x, z]), 0.25f, true);
            else pieceCommandManager.EnQueueRoboticArmMove(pieces[x, z], piece_grave.GetAIGravePos(pieces[x, z]), 0.4f, true);
        }

instance 생성은 EnQueuePlayerMove()에서 처리됨. true, false 매개변수가 추가됨.

    /// <summary>
    /// 각 명령이 끝나면 호출되어 다음 명령을 실행하는 함수
    /// </summary>
    public void ExecuteNextCommand()
    {
        if (isWorking) return;

        if (moveQueue.Count > 0)
        {
            ICommand command = moveQueue.Dequeue();
            willEndTurn = command.WillEndTurn();
            command.Execute();

            isWorking = true;
        }
        else if (willEndTurn)
        {
            gameManager.EndTurn();
        }
    }

PieceCommandManager.cs

손가락이 아파서 오늘은 여기까지

profile
너 정말 **핵심**을 찔렀어

0개의 댓글