Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > .NET Frameworks (точка нет фреймворки) > C# (си шарп)
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 14.04.2016, 19:05   #1
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию Проблема с многопоточностью

Уважаемые форумчане. Помогите решить проблему.
Хочу попробовать написать какой-то простейший аналог рпг. Суть проста. Есть локация, на ней находятся агрессивные и не агрессивные мобы. На данный момент пробую организовать атаку агрессивным мобом неагрессивного. Вот часть наработок, касающихся сего.
Код:
class Program
{
    static void Main(string[] args)
    {
        Location a = new Location();//создают локацию
        Console.ReadLine();
        while (true)
        {
            foreach (Mob mob in a.Monsters)
            {
                mob.Thinking();//заставляю мобов думать по очереди
            }
         }
    }
}
public class Location       //класс локации
{
   public List<Mob> Monsters { get; private set; }     //список мобов на карте
   public Location()
   {
       Monsters = new List<Mob>();// список мобов, обитающих на локации
       string monstrType1 = "1 Poring 1 0 Plant Water Medium Aggresive,Looter 2,1,1,0,6,5 2 5 2 60";//всякие характеристики агрессивного моба
       string monstrType2 = "1 Poring 1 0 Plant Water Medium Looter 2,1,1,0,6,5 2 5 2 60";// всякие характеристики пассивного моба
       Monsters.Add(new Mob(monstrType1, 0, this));
       Monsters.Add(new Mob(monstrType2, 1, this));//потом всё это будет просто в цикле.
    }
}
public class Mob
{
    public bool IsAlive { get; private set; }       //жив или нет
    private State currentState; //чем моб сейчас занят
    public Dictionary<Mob, int> WhoAround { get; private set; }  //список кто вокруг и квадрат расстояния
    public Mob Target { get; private set; } //цель моба
    public readonly Location CurLoc;    //локация, на которой находится моб
    public int OnMapId;     //id монстра на карте
    public int SightRange { get; private set; }  //радиус обзора моба
    public int AttackRange { get; private set; }  //дальность атаки моба
    public Coordinates CurCoord;   //координаты моба на карте
    private Coordinates NextCoord;  //координаты, на которые идет моб
    private CancellationTokenSource stateStop;
    public int Atk { get; private set; }        //физ.атака моба
    public int Aspd { get; private set; }       //скорость атаки моба
    public int CurrentHP { get; private set; }  //текущее хп моба
    public void Thinking()
    {
        if (CurrentHP <= 0) IsAlive = false;
        if (IsAlive&&currentState!=State.Atack&&currentState!=State.CastSpell)
        {
            LookAround();
            if (mobBehavior.Aggresive)
            {
                if (WhoAround.Count!=0)
                {
                    foreach (KeyValuePair<Mob, int> anyone in WhoAround)
                    {
                       if (Target == null) Target = (anyone.Key.CurrentHP > 0) ? anyone.Key : null;
                       else
                       {
                           int targetRange;
                           if (WhoAround.TryGetValue(Target, out targetRange))
                           {
                           if (targetRange > anyone.Value) Target = anyone.Key;
                           }
                           else Target = anyone.Key;
                        }
                    }
                }
            }
            switch (currentState)
            {
                case State.Idle:        //моб ничего не делает
                if (Target != null)       //если есть цель...
                {
                    if (WhoAround[Target] < AttackRange)
                    {
                        stateStop.Cancel();
                        Attack();      //и она ближе, чем дальность атаки - атаковать ее
                        Console.WriteLine("{0} atack {1}", this, Target);
                    }
                    else if (MoveSpeed != 0)
                    {
                        if (NextCoord!=Target.CurCoord)
                        {
                            NextCoord = Target.CurCoord;
                            ToMove(NextCoord);     //а если дальше и моб может ходить, идти к ней
                         }
                     }
                }
                else
                {
                    if (MoveSpeed != 0)   //если цели нет, то просто может пойти куда-нибудь
                    {
                       if (r.Next(4) == 3)       //с вероятностью в 25% моб пойдет по случайным координатам в радиусе видимости
                        {
                            NextCoord = new Coordinates(CurCoord.X + r.Next(SightRange * 2) - SightRange, CurCoord.Y + r.Next(SightRange * 2) - SightRange);
                            ToMove(NextCoord);
                         }
                    }
                    else DoNothing();
                }
                break;
                case State.Move:        //моб идет
                   if (Target != null)       //если есть цель...
                   {
                       stateStop.Cancel();     //останавливаем передвижение и проверяем....
                       if (WhoAround[Target] < AttackRange)
                       {
                           Attack();      //если цель ближе, чем дальность атаки - атаковать ее
                       }
                       else if (MoveSpeed != 0)
                         {
                             if (NextCoord != Target.CurCoord)
                             {
                                NextCoord = Target.CurCoord;
                                ToMove(NextCoord);     //а если дальше и моб может ходить, идти к ней
                              }
                         }
                    }
                    else
                    {
                        if (NextCoord == CurCoord) DoNothing();
                    }
                    break;
               case State.Atack:       //моб атакует
                    if (!Target.IsAlive)
                    {
                        stateStop.Cancel();
                        DoNothing();
                    }
                    else
                    {
                        if (WhoAround[Target]>=AttackRange)
                        {
                           stateStop.Cancel();
                           NextCoord = Target.CurCoord;
                           ToMove(NextCoord);     //а если дальше и моб может ходить, идти к ней
                         }
                    }
                    break;
               case State.CastSpell:   //моб кастует скилл
                    break;
            }
        }
    }
    public async void ToMove(Coordinates target)
    {
       currentState = State.Move;
       try
       {
           await Task.Run(() =>
           {
              while (CurCoord != target)
              {
                  Step(target);
              }
           }, stateStop.Token);
       }
       catch (TaskCanceledException)
       {
           Console.WriteLine("{0} stop.", this);
       }
    }
    public async void Attack()
    {
       currentState = State.Atack;
       try
       {
          await Task.Run(() =>
          {
              Thread.Sleep(500 + (200 - Aspd));
              Target.CurrentHP -= Atk;
           }, stateStop.Token);
        }
        catch (TaskCanceledException)
        {
           Console.WriteLine("{0} stop attack.", this);
        }
    }
}
Постарался оставить только основные части, но мог что-то упустить, поскольку пишу с телефона (на данный момент доступа к компу нет). Полный текст кода на github'е.
Так вот. Проблема в следующем. Когда агрессивный моб подходит к пассивному, то успевает один раз ударить и все. Потом пассивный начинает идти бесконечно, а при отладке получается, что код в метод Thinking() больше не заходит.
Может кто подскажет, в чем я не прав.
max_prorok вне форума Ответить с цитированием
Старый 15.04.2016, 13:15   #2
Akeloya
Форумчанин
 
Регистрация: 27.01.2014
Сообщений: 115
По умолчанию

Давайте блок-схему
Решать вопрос "почему" когда речь идет о коде и каком-то алгоритме можно только тогда, когда у алгоритма есть какое-то оформление. Если у вас кроме кода ничего нет - значит код написан правильно, а вот вы неправильно его интерпретируете

Можно зайти с обратной стороны - вы дали кусок кода, я восстановил по нему алгоритм, проверяю - совпадает, задаю вопрос - а что не так? Вы пишите, что не так, но это не соответствует алгоритму, в первую очередь.

Хотя бы на псевдокоде напишите, что ли, сидеть разбираться с кодом без эталона вообще никто не будет, я думаю.
Akeloya вне форума Ответить с цитированием
Старый 15.04.2016, 14:17   #3
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию

Я пытаюсь реализвать стандартную машину состояний. В первом приюлижении, есть перечисление состояний State {Idle; Move; Atack}. Для простоты CastSpell пока не трогаем. В классе моба есть переменная currentState, которая отображает то, чем моб занимается на данный момент.
- Idle - ничего не делает;
- Move - двигается;
- Atack - наносит физический урон.
В методе Thinking() описан алгоритм действий моба. Допустим, он ничем не занят. Тогда у него есть несколько вариантов развития.
1) с шансом в 25% он может прогуляться в соседние координаты в радиусе видимости (переход в состояние Move), в оставшиеся 75% - он останется на месте, и так же продолжит ничего не делать (остается в состоянии Idle).
2) Если он обнаруживает, что в радиусе его видимости есть враг (Target), и враг находится на расстоянии меньшем, чем радиус атаки - то моб начинает его бить. (переходит в состояние Atack)
3)Если он обнаруживает в радиусе видимости врага, но он дальше чем, дальность атаки, то он будет подходить к врагу (переходит в состояние Move).
В принципе все логично. Теперь рассмотрим состояние Move.
1) Моб идет, и доходит до координат на которые он двигался, то он заканчивает движение (переходит в состояние Idle).
2) Моб идет, и вдруг увидел врага, но он дальше чем дальность атаки, то начинает приближаться к нему, или вдруг враг переместился на другую координату, то моб меняет конечные координаты своего пути, дабы преследовать врага (остается в Move, но меняет координаты)
3) Моб подходит к врагу на расстояние атаки, и начинает атаковать его (переходит в Atack).
Ну и 4 положение - Atack:
1) Если цель умерла, то стоит просто так (переходит в состояние Idle).
2) Если цель выбежала за пределы атаки, начинает преследовать ее (переходит в Move).
3) Если цель стоит в радиусе атаки, то потрошит ее до смерти (остается в состоянии Atack).
Вот тут я сейчас глянул, и действительно косяк. У меня нет цикла в Task`е внутри Atack(). Но не суть. Проблема в другом. Получается, что после одного удара у меня Task заканчивается Exception`ом, т.е. пишется строка "{0} stop attack.", но после этого цель начинает все время куда уходить, а моб который его атаковал, остался стоять на месте. Скажу большее. После вызова Exception`а в Atack(), у меня код вообще больше не заходит в Thinking(), как будто главный поток где-то застрял.

Добавил картиночку для большей наглядности.


P.S. Спасибо большое за помощь. Боялся, что мало кто откликнется, увидев такое большое количество букв.

Последний раз редактировалось max_prorok; 15.04.2016 в 15:13. Причина: Дописал благодарность
max_prorok вне форума Ответить с цитированием
Старый 15.04.2016, 22:10   #4
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию

Кажется нашел проблему. Проблема заключается в следующем. После того, как у меня агрессивный моб подходит к пассивному на расстояние удара, его передвижение в параллельном потоке останавливается (по средством CancellationTokenSource stateStop) и вызывается асинхронный метод Atack(), в котором выполняется Task.Run() до того момента, пока цель не умрет, или пока не будет отмены с помощью все того же stateStop. Только вот в нем в свойстве IsCancellationRequest уже стоит true, и получается, что как только код заходит в Task.Run(), то тут же выходит с Exception`ом. Можно ли как-нибудь поменять это значение, кроме как убиения токена и заново вызывать его конструктор?
max_prorok вне форума Ответить с цитированием
Старый 22.04.2016, 14:08   #5
the_deer_one
Участник клуба
 
Аватар для the_deer_one
 
Регистрация: 04.04.2010
Сообщений: 1,554
По умолчанию

А нафига тебе параллельные потоки? 0_0
Делай всё в одном.
the_deer_one вне форума Ответить с цитированием
Старый 22.04.2016, 14:41   #6
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию

Эт как же так??? Представь, есть у тебя 10 локаций, на каждой по 50 мобов. И что? Пока один моб не пройдет три клетки на какой-нибудь локации, все другие будут стоять мясом? Или другой вариант. Ты подходишь, начинаешь бить моба, он начинает бить в ответ. А вокруг все остальные мобы просто стоят в сторонке и смотрят бой не на жизнь, а на смерть. И видимо ставки делают, кто кого убьет в конце концов.
max_prorok вне форума Ответить с цитированием
Старый 22.04.2016, 15:11   #7
the_deer_one
Участник клуба
 
Аватар для the_deer_one
 
Регистрация: 04.04.2010
Сообщений: 1,554
По умолчанию

max_prorok В квейк 2 играл? Он в одном потоке работает.


1. Считываешь инпут.
2. Просчитываешь для всех мобов поведение.
3. Двигаешь их.
И по кругу.

А треды тяжёлые, и не предсказуемые. Фиг знает когда и какому треду и в какой момент ОС передаст управление.
the_deer_one вне форума Ответить с цитированием
Старый 22.04.2016, 16:55   #8
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию

Тогда не совсем понятно, как контролировать время... Чтобы мобы не портались по локации???
max_prorok вне форума Ответить с цитированием
Старый 22.04.2016, 17:04   #9
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
Сообщение от max_prorok Посмотреть сообщение
Тогда не совсем понятно, как контролировать время... Чтобы мобы не портались по локации???
есть понятие игровой тик(он же UPS - Updates per second), минимальное время обработки физики/ИИ и прочего.
часто это 60 раз в секунду, dt=1sec/60.
сейчас в некоторых играх разделяют эти понятия(FPS и UPS) явно.

в вашем случае у вас сервер же?
в любом случае внутри локации параллельность не хорошо. больше потеряете на синхронизации.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 22.06.2016, 12:05   #10
max_prorok
Форумчанин
 
Регистрация: 06.10.2011
Сообщений: 181
По умолчанию

Прошу прощения, что поднимаю старую и забытую тему.
Но что-то я не могу допереть до конца как реализовать UPS.
Точнее я понял, как его реализовать, но не понял как потом наложить действия.
Т.е. после создания локации запускаем (псевдо)бесконечный цикл, в котором:
1. Каждый моб (персонаж пока не трогаем) думает, и если что, меняет свой статус.
2. В зависимости от статуса, он либо продолжает делать то, что делал, либо начинает новое действо.
Но вот вопрос по поводу пункта 2. Локация разделена на клетки (скажем 200х200). Скажем, на переход с одной клетки на другую мобу необходимо потратить 400 мс. А при атаке задержка между ударами составляет 200 мс.
Как это чудо организовать в одном потоке. Добавлять в класс моба какой-то счетчик (если конечно таковой имеется)?
Или может вернее сделать это как-то на событиях?
Только подпните меня в нужном направлении. Очень хочется именно самому допереть до конца, а не взять готовый кусок и скопи-пастить его.
max_prorok вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Снова я и снова геморрой, только уже с многопоточностью FleXik Общие вопросы Delphi 26 07.07.2013 16:48
Неблокирующий сокет Си проблема с закрытием, проблема с закрытием сокетов в цикле mnx_vol C/C++ Сетевое программирование 0 06.11.2012 13:57
нужно разобраться с многопоточностью с ThRead Object Толян1 C++ Builder 1 16.07.2012 09:52
Проблема с многопоточностью Kapitan4ik Работа с сетью в Delphi 3 29.02.2012 21:46