Форум программистов, компьютерный форум, киберфорум
Unity, Unity3D
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.62/13: Рейтинг темы: голосов - 13, средняя оценка - 4.62
 Аватар для Abomination_25
4 / 4 / 0
Регистрация: 13.12.2016
Сообщений: 246

Попытка написать машину состояний(персонажа,врагов и прочее) - RPG

17.02.2018, 01:47. Показов 2837. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем привет, начал заниматься в последнее время рефакторингом свое кода. И понял, что без GameManager'ов никак не обойтись. Пока написал свой небольшой менеджер по вводу, он вообще не блещет ничем примечательным, поэтому хочется показать и спросить, а как лучше сделать, если не так? Так же начал писать основной класс для состояний - от которого будут наследоваться такие классы как PlaeyrStates, EnemyStates и прочее. Основа выглядит так:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class States  {
 
    protected bool state;
    protected string _name;
 
 
    public States(){
        state = false;
        _name = "Empty";
    }
 
    public States(string Name,bool value){
        state = value;
        _name = Name;
    }
        
 
    public bool getState(){
        
        return state;
    }
 
    public string getName(){
        return _name;
    }
 
    public void setStateValue(bool value){
        
    state = value;
            
    }
 
    public void setStateName(string Name){
 
        _name = Name;
 
    }
 
}
Он лежит просто в ассетах, от него уже на Player вешаю PlayerStates:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
 
public class PlayerStates : MonoBehaviour {
 
    public Text Display;
    public string [] Name;
    private bool[] Value;
    int size;
    string currentName;
    States [] Player;
    CharacterController controller;
    InputManager input;
    PlayerStats stats;
 
 
    void Awake(){
        controller = GetComponent<CharacterController> ();
        stats = GetComponent<PlayerStats> ();
        input = GameObject.Find ("InputManager").GetComponent<InputManager> ();
        size = Name.Length;
        Value = new bool[size];
        Player = new States[size];
        for (int i = 0; i < size; i++) {
            Value [i] = BasicStatesByName (Name [i]);
        }
        for (int i = 0; i < size; i++) {
            Player [i] = new States (Name [i], Value [i]);
        }
 
    }
 
    void Start(){
        Display.text = currentState ();
    }
 
 
    void Update(){
        UpdatePlayerStates ();
        Display.text = currentState ();
    }
 
    public bool getState(string stateName){
        bool value = false;
        for (int i = 0; i < size; i++) {
            if (stateName == Player [i].getName ()) {
                value = Player [i].getState ();
            }
        }
        return value;
    }
 
    public string getName(int index){
        return Player [index].getName();
    }
 
    string currentState(){
        for (int i = 0; i < size; i++) {
            if (Player [i].getState ()) {
                currentName = Player [i].getName ();
            }
        }
        return currentName;
    }
 
    void UpdatePlayerStates(){
        for (int i = 0; i < size; i++) {
            Player[i].setStateValue(BasicStatesByName (Player[i].getName()));
        }
    }
 
    bool BasicStatesByName(string s){
        bool value=false;
        switch (s) {
        case "Attack":
            {
                Debug.Log (stats.delayed() > Time.time);
                value=(input.getButtonState("Attack") && stats.canAttack() && !BasicStatesByName("Falling") && !(stats.canJump() && input.getButtonState("Jump")));
                break;
            }
        case "Walking":
            {
                value=(!BasicStatesByName("Attack") &&  !BasicStatesByName("Falling") && (input.getHor()!=0 || input.getVert()!=0) && !input.getButtonState("Sprint"));
                break;
            }
        case "Running":
            {
                value=(!BasicStatesByName("Falling") && (input.getHor()!=0 || input.getVert()!=0) && input.getButtonState("Sprint") && !BasicStatesByName("Attack") );
                break;
            }
        case "Jumping":
            {
                value=(!BasicStatesByName("Falling") && stats.canJump() && !BasicStatesByName("Attack") && input.getButtonState("Jump"));
                break;
            }
        case "Falling":
            {
                value=!controller.isGrounded;
                break;
            }
        case "Idle":
            {
                value =(input.getHor () == 0 && input.getVert()==0 && !BasicStatesByName("Falling") && ! BasicStatesByName("Jumping") && !BasicStatesByName("Attack"));
                break;
            }
        }
        return value;
    }
}
Реализация кривая - знаю, сам морочился с ним. Я вывод состяний делаю в виде UI Text на экран. Вроде работает, но надо поработать еще на тем, чтобы любое состояние можно было легко добавить, а так же вычислить функцию ( ну булевое значение) легко. Пока что мои состояния ограничеными кейсами и соотвественно, если мне надо будет добавить новое состояние, то придется лезть в код и добавлять. Может кто-то знает как лучше реализовать эту систему?? Да, и следует ли делать базовый класс от MonoBehaviour, а наследников от базового только? Просто весь код был раньше - ужасная куча мусора

Добавлено через 5 минут
А теперь более менее, но все равно хочется довести до ума это дело. Основная задача - получить доступ к вводу игрока в любом скрипте без проблема и так же быть с состояниями, но там можно и через getComponent<>(); Мне допустим для аниматора удобно использовать состояния, просто запросил булеву переменную по имени состояния - и поставил ее в аниматор. Так же и для различных функции это мне нужно. Вот InputManager - сразу говорю, что он почти, что нет. Но им я пользуюсь так - ищу его по имени Find("InputManager") - можно сделать как-то по-другому? Потому что инпут мне везде нужен, допустим в том же PlayerStates я булевые переменные считаю через InputManager - то есть придется создавать переменную и getcomponent<>() делать... В общем много сейчас проблема с этой машиной состояния. Хотелось бы сюда выкладывать наработки и улучшать)
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class InputManager : MonoBehaviour {
 
    struct MyButtons{
        public string name;
        public bool state;
    }
 
    private float vertical;
    private float horizontal;
    private float mouseX;
    private float mouseY;
 
    public int NumberOfButtons=8;
    private MyButtons[] buttons;
    public string[] buttonsName;   
    private bool[] buttonsState; 
    public KeyCode[] ActionButtons;
 
    void Awake(){
        //ActionButtons = new KeyCode[NumberOfButtons];
        buttons = new MyButtons[NumberOfButtons];
        //buttonsName = new string[NumberOfButtons];
        buttonsState = new bool[NumberOfButtons];
        for (int i = 0; i < NumberOfButtons; i++) {
            buttons [i].name = buttonsName [i];
            buttons [i].state = buttonsState [i];
        }
    }
 
    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        //WASD
        vertical = Input.GetAxis ("Vertical");
        horizontal = Input.GetAxis ("Horizontal");
        //Mouse
        mouseX=Input.GetAxis("Mouse X");
        mouseY=Input.GetAxis("Mouse Y");
        //ActionButtons
        CheckButtonStates();
 
    }
 
    void CheckButtonStates(){
        for (int i = 0; i < NumberOfButtons; i++) {
            buttons [i].state = false;
            if (getButtonName (i) != "Sprint") {
                if (Input.GetKeyDown (ActionButtons [i])) {
                    setButtonState (getButtonName (i));
                }
            } else if (Input.GetKey (ActionButtons [i])) {
                setButtonState (getButtonName (i));
            } 
        }
    }
 
    public float getVert(){
        return vertical;
    }
 
    public float getHor(){
        return horizontal;
    }
 
    public float getMouseX(){
        return mouseX;
    }
 
    public float getMouseY(){
        return mouseY;
    }
        
    public string getButtonName(int i){
        return buttons [i].name;
    }
 
    public bool getButtonState(string Name){
        bool value=false;
        for (int i = 0; i < NumberOfButtons; i++) {
            if (Name == buttons [i].name) {
                value = buttons [i].state;
            }
            
        }
        return value;
    }
 
    private void setButtonState(string Name){
        for (int i = 0; i < NumberOfButtons; i++) {
            if (Name == buttons [i].name) {
                buttons [i].state = true;
            }
        }
    }
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
17.02.2018, 01:47
Ответы с готовыми решениями:

Pool Врагов в зависимости от положения персонажа
Здравствуйте всем. Google ни чем не помог, обращаюсь к вам. Есть 8 врагов (Создаются при старте), 40 GameObject заранее...

Игра с перемещением персонажа и врагов
Нужно написать программу, с полем 80x80. Программа должна выбивать на экран информацию о местоположении Главного героя и трех врагов. Также...

Как реализовать машину состояний (FSM)
Пишу игру. Реализовал простую машину состояний: public enum GameState{Preview, Menu, Game} private static GameState state; public...

8
 Аватар для Cr0c
753 / 600 / 204
Регистрация: 06.08.2015
Сообщений: 2,432
17.02.2018, 09:19
C#
1
2
3
4
5
6
class InputManager : Monobehavior {
 public InputManager Instance { private set; get; }
 void Awake() {
  Instance = this;
  // остальной код
 }
Потом доступ через InputManager.Instance к нему. Это называется синглтон.

Добавлено через 4 минуты
Для стейта лучше енум, а не стринг. Сделай тогда уже класс
C#
1
2
3
4
5
[System.Serializable]
class ObjectState {
 public StateEnum state;
 public bool value;
}
И делай массив из него, так удобнее и проще.

Добавлено через 8 минут
Енумы всегда надо делать со значениямм
C#
1
2
3
4
5
enum StateEnum {
 None = 0,
 Walk = 1,
 Attack = 2
}
0
 Аватар для Abomination_25
4 / 4 / 0
Регистрация: 13.12.2016
Сообщений: 246
17.02.2018, 11:42  [ТС]
Спасибо, а сам класс инпут менеджера, нормально вышел, то есть остальной код? И вот как со стейтами быть не знаю, ведь допустим каждый стейтами зависит от другого и каждому надо присвоить функцию булеву, я хоть что то нормальное сделал в своём коде? Да, а массив делать уже стоит из стейтов в скрипте для игрока? Спасибо вам за ответ)
0
 Аватар для Cr0c
753 / 600 / 204
Регистрация: 06.08.2015
Сообщений: 2,432
17.02.2018, 14:24
Я не знаю что там хотелось получить. Но я иначе бы делал. Более обощенно.
0
 Аватар для Abomination_25
4 / 4 / 0
Регистрация: 13.12.2016
Сообщений: 246
17.02.2018, 14:49  [ТС]
Хотел инпут сделать синглтоном, чтобы доступ из любого скрипта без GetComponent () получить. А стейты хотел сделать для аниматора и для передвижения персонажа. Тоже самое и для врагов. Общий стейт я понял как написать, а вот не подскажете как лучше поступить: у меня много стейтов будет, то есть мне их лучше присвоить? Я же массив создам потом Object State и буду туда заносить стейты с их значениями, а вот значений ведь зависят от функций, то есть где-то эти функции придётся прописать... Вот не пойму где лучше и как сделать так, чтобы любой стейт новый можно было без проблем добавить. А так же хотелось бы иметь переменную, которая отвечала бы за текущий стейт, но как его получить не очень понимаю) или же стоит заранее определиться с количеством стейтов и все прописать?
0
 Аватар для Cr0c
753 / 600 / 204
Регистрация: 06.08.2015
Сообщений: 2,432
17.02.2018, 15:05
Без блендинга - заранее определится. И в инпуте всё равно ручками каждый раз писать, это не сильно унифицируется, это надо специально считать. Например, класс с осью (string), триггером (bool), направлением (bool) и делегатом (delegate Control(float value)) для вызова или ивентом, чтобы в инспекторе настроить. Заполняешь данные, кодом задаешь делегаты - и вот тебе более-менее универсальный контроллер ваода.
0
 Аватар для Abomination_25
4 / 4 / 0
Регистрация: 13.12.2016
Сообщений: 246
17.02.2018, 15:11  [ТС]
Класс для осей создать? Не очень понял... А про делегаты первый раз слышу... Надо бы почитать... То есть в инпут нужно ещё несколько классов добавить?
0
 Аватар для Cr0c
753 / 600 / 204
Регистрация: 06.08.2015
Сообщений: 2,432
17.02.2018, 16:16
C#
1
2
3
4
5
6
7
8
9
public delegate AxisEvent(float value) = delegate {};
class AxisData {
 public string axisName;
 public bool isTrigger = false;
 public bool positiveCheck = true;
 public bool negativeCheck = true;
 public bool pressed = false;
 public AxisEvent ProcessAxis;
}
Проверять ось, проверять по флагам разрешение события, запоминать предыдущее состояние, проверять флаг триггера. Массив этого класса и циклом перебирать. Тогда просто настройкой массива можно расширять ввод. Ну а реализация - это в любом случае руками, как и задание делегатов.
C#
1
2
3
4
5
6
7
8
9
10
11
public AxisData[] axises;
void Update() {
 foreach (AxisData ad in axises) {
  float data = Input.GetAxis(ad.axisName);
  if ((ad.isTrigger && !ad.pressed) || (!ad.isTrigger))
   if ((ad.positiveCheck && data > 0f) || (ad.negativeCheck && data < 0f))
    ProcessAxis(data);
  if (ad.data != 0f) { ad.presses = true; }
  else { ad.pressed = false; }
 }
}
Вроде так, с телефона набирал.

Добавлено через 23 минуты
В реализации делаешь по методу на каждую ось типа
C#
1
2
3
4
public void AxisVertical(float value) {// some code};
void Init() {
 axises[0].ProcessAxis += AxisVertical;
}
0
 Аватар для Abomination_25
4 / 4 / 0
Регистрация: 13.12.2016
Сообщений: 246
17.02.2018, 20:48  [ТС]
Спасибо) Более менее понятно стало)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
17.02.2018, 20:48
Помогаю со студенческими работами здесь

Как написать корень и прочее math
Как написать y в степени кубический корень из модуля x и прочее, по ссылке в описании Я не совсем понял, как написать у со степенью и...

Попытка написать простенькую БД
Хочу попробовать написать простенькую БД, но есть некоторые вопросы по реализации. Немогу придумать как реализовать: У каждой ячейки...

System.AccessViolationException: Попытка чтения или записи в защищенную память - попытка 2
В предыдущей теме некто вообразил, что код зацикливается, поэтому приходится выкладывать более широкую модель. Надеюсь, до целого проекта...

System.AccessViolationException: Попытка чтения или записи в защищенную память - попытка 3
Уже простите за некомпилируемый код, просто я переустановил Windows, и Visual Studio удалилась, только сегодня днем установил. Код там...

Написать программу, которая принимает решение о действии персонажа на игровом поле.
Написать программу, которая принимает решение о действии персонажа на игровом поле.


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
Кому нужен AOT?
DevAlt 26.03.2026
Решил сделать простой ланчер Написал заготовку: dotnet new console --aot -o UrlHandler var items = args. Split(":"); var tag = items; var id = items; var executable = args;. . .
Отправка уведомления на почту при изменении наименования справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере изменения наименования типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru