En aquest projecte, implementarem un personatge no jugador (NPC) que utilitza tècniques de presa de decisions per comportar-se de manera intel·ligent en un entorn de joc. Utilitzarem màquines d'estats finits (FSM) i arbres de decisió per gestionar el comportament de l’NPC.

Objectius del Projecte

  1. Comprendre les tècniques de presa de decisions: Aprendre com les màquines d'estats finits i els arbres de decisió poden ser utilitzats per gestionar el comportament dels NPCs.
  2. Implementar una FSM per a un NPC: Crear una màquina d'estats finits que defineixi diferents estats i transicions per a un NPC.
  3. Implementar un arbre de decisió per a un NPC: Desenvolupar un arbre de decisió que permeti a l’NPC prendre decisions basades en diferents condicions.
  4. Integrar la FSM i l'arbre de decisió en un motor de joc: Implementar aquestes tècniques en un motor de joc com Unity o Unreal Engine.

Requisits Previs

  • Coneixements bàsics de programació (preferiblement en C# per Unity o C++ per Unreal Engine).
  • Familiaritat amb el motor de joc que s’utilitzarà.
  • Comprensió dels conceptes de màquines d'estats finits i arbres de decisió.

Passos del Projecte

  1. Definició del Comportament de l’NPC

Abans de començar a programar, definirem el comportament que volem que tingui l’NPC. Per exemple, un guardià que patrulla una àrea, persegueix intrusos i torna a la patrulla quan no hi ha intrusos.

Comportaments de l’NPC:

  • Patrullar: L’NPC es mou entre diferents punts de patrulla.
  • Perseguir: L’NPC persegueix un intrús quan el detecta.
  • Tornar a la Patrulla: L’NPC torna al punt de patrulla més proper quan perd de vista l’intrús.

  1. Implementació de la FSM

2.1. Definició dels Estats i Transicions

Definirem els estats i les transicions per a la FSM del nostre NPC.

Estats:

  • Patrullar
  • Perseguir
  • Tornar a la Patrulla

Transicions:

  • De Patrullar a Perseguir: Quan es detecta un intrús.
  • De Perseguir a Tornar a la Patrulla: Quan es perd de vista l’intrús.
  • De Tornar a la Patrulla a Patrullar: Quan l’NPC arriba al punt de patrulla.

2.2. Implementació en Codi

A continuació, implementarem la FSM en C# per Unity.

using UnityEngine;
using System.Collections;

public class NPCController : MonoBehaviour
{
    private enum State { Patrol, Chase, ReturnToPatrol }
    private State currentState;

    public Transform[] patrolPoints;
    private int currentPatrolIndex;
    public Transform target;
    public float chaseRange = 5f;
    public float loseSightRange = 10f;

    void Start()
    {
        currentState = State.Patrol;
        currentPatrolIndex = 0;
    }

    void Update()
    {
        switch (currentState)
        {
            case State.Patrol:
                Patrol();
                break;
            case State.Chase:
                Chase();
                break;
            case State.ReturnToPatrol:
                ReturnToPatrol();
                break;
        }
    }

    void Patrol()
    {
        Transform patrolPoint = patrolPoints[currentPatrolIndex];
        MoveTowards(patrolPoint.position);

        if (Vector3.Distance(transform.position, patrolPoint.position) < 0.5f)
        {
            currentPatrolIndex = (currentPatrolIndex + 1) % patrolPoints.Length;
        }

        if (Vector3.Distance(transform.position, target.position) < chaseRange)
        {
            currentState = State.Chase;
        }
    }

    void Chase()
    {
        MoveTowards(target.position);

        if (Vector3.Distance(transform.position, target.position) > loseSightRange)
        {
            currentState = State.ReturnToPatrol;
        }
    }

    void ReturnToPatrol()
    {
        Transform patrolPoint = patrolPoints[currentPatrolIndex];
        MoveTowards(patrolPoint.position);

        if (Vector3.Distance(transform.position, patrolPoint.position) < 0.5f)
        {
            currentState = State.Patrol;
        }
    }

    void MoveTowards(Vector3 destination)
    {
        Vector3 direction = (destination - transform.position).normalized;
        transform.position += direction * Time.deltaTime * 2f; // Adjust speed as needed
    }
}

  1. Implementació de l'Arbre de Decisió

3.1. Definició de les Condicions i Accions

Definirem les condicions i accions per a l’arbre de decisió del nostre NPC.

Condicions:

  • Detecta Intrús
  • Perd de Vista l’Intrús

Accions:

  • Patrullar
  • Perseguir
  • Tornar a la Patrulla

3.2. Implementació en Codi

A continuació, implementarem l’arbre de decisió en C# per Unity.

using UnityEngine;

public class NPCDecisionTree : MonoBehaviour
{
    private abstract class DecisionNode
    {
        public abstract void Execute(NPCDecisionTree npc);
    }

    private class ActionNode : DecisionNode
    {
        private System.Action<NPCDecisionTree> action;

        public ActionNode(System.Action<NPCDecisionTree> action)
        {
            this.action = action;
        }

        public override void Execute(NPCDecisionTree npc)
        {
            action(npc);
        }
    }

    private class ConditionNode : DecisionNode
    {
        private System.Func<NPCDecisionTree, bool> condition;
        private DecisionNode trueNode;
        private DecisionNode falseNode;

        public ConditionNode(System.Func<NPCDecisionTree, bool> condition, DecisionNode trueNode, DecisionNode falseNode)
        {
            this.condition = condition;
            this.trueNode = trueNode;
            this.falseNode = falseNode;
        }

        public override void Execute(NPCDecisionTree npc)
        {
            if (condition(npc))
            {
                trueNode.Execute(npc);
            }
            else
            {
                falseNode.Execute(npc);
            }
        }
    }

    public Transform[] patrolPoints;
    private int currentPatrolIndex;
    public Transform target;
    public float chaseRange = 5f;
    public float loseSightRange = 10f;

    private DecisionNode rootNode;

    void Start()
    {
        currentPatrolIndex = 0;

        // Build the decision tree
        rootNode = new ConditionNode(
            DetectsIntruder,
            new ActionNode(Chase),
            new ConditionNode(
                LostSightOfIntruder,
                new ActionNode(ReturnToPatrol),
                new ActionNode(Patrol)
            )
        );
    }

    void Update()
    {
        rootNode.Execute(this);
    }

    bool DetectsIntruder(NPCDecisionTree npc)
    {
        return Vector3.Distance(npc.transform.position, npc.target.position) < npc.chaseRange;
    }

    bool LostSightOfIntruder(NPCDecisionTree npc)
    {
        return Vector3.Distance(npc.transform.position, npc.target.position) > npc.loseSightRange;
    }

    void Patrol(NPCDecisionTree npc)
    {
        Transform patrolPoint = npc.patrolPoints[npc.currentPatrolIndex];
        MoveTowards(patrolPoint.position);

        if (Vector3.Distance(npc.transform.position, patrolPoint.position) < 0.5f)
        {
            npc.currentPatrolIndex = (npc.currentPatrolIndex + 1) % npc.patrolPoints.Length;
        }
    }

    void Chase(NPCDecisionTree npc)
    {
        MoveTowards(npc.target.position);
    }

    void ReturnToPatrol(NPCDecisionTree npc)
    {
        Transform patrolPoint = npc.patrolPoints[npc.currentPatrolIndex];
        MoveTowards(patrolPoint.position);

        if (Vector3.Distance(npc.transform.position, patrolPoint.position) < 0.5f)
        {
            npc.currentPatrolIndex = (npc.currentPatrolIndex + 1) % npc.patrolPoints.Length;
        }
    }

    void MoveTowards(Vector3 destination)
    {
        Vector3 direction = (destination - transform.position).normalized;
        transform.position += direction * Time.deltaTime * 2f; // Adjust speed as needed
    }
}

  1. Integració i Proves

  • Integració: Assegura’t que el codi de la FSM i l’arbre de decisió estiguin correctament integrats en el motor de joc.
  • Proves: Prova el comportament de l’NPC en diferents escenaris per assegurar-te que les transicions i les accions es realitzen correctament.

  1. Optimització i Millores

  • Optimització: Revisa el codi per optimitzar el rendiment, especialment en escenaris amb múltiples NPCs.
  • Millores: Considera afegir més estats o condicions per fer el comportament de l’NPC més complex i realista.

Conclusió

En aquest projecte, hem creat un NPC que utilitza tècniques de presa de decisions per comportar-se de manera intel·ligent en un entorn de joc. Hem implementat una màquina d'estats finits i un arbre de decisió, i hem integrat aquestes tècniques en un motor de joc. Aquest projecte proporciona una base sòlida per desenvolupar comportaments més complexos i realistes per als NPCs en futurs jocs.

© Copyright 2024. Tots els drets reservats