Застрял в создании движка движения в 2D-игре

Я делаю движок для своей 2D-игры с видом сверху, но застрял, пытаясь решить следующую проблему:

  • Игрок может перемещаться с помощью клавиш со стрелками, которые ускоряют вас в соответствующих направлениях. Существует трение, поэтому вы перестаете двигаться после отпускания клавиш, хотя и не сразу.
  • Когда вы удерживаете две перпендикулярные клавиши, вы ускоряетесь в этом направлении на 45 ° с той же скоростью, что и на одной оси.
  • Существует максимальная скорость, выше которой вы не можете ускориться при ходьбе, очевидно, что это также ограничивает вашу максимальную скорость ходьбы. Вы можете быть сбиты с толку и тем самым превысить эту скорость.
  • Если вы двигаетесь быстрее макс. walkpeed, вы можете замедлиться быстрее, если будете удерживать клавиши в противоположном (-ых) направлении (-ях)

Псевдокод для первой точки, без трения:

gameTick(){

  tempX += LeftKeyHeld ? -1 : 0;
  tempX += RightKeyHeld ? 1 : 0;
  tempY += UpKeyHeld ? -1 : 0;
  tempY += DownKeyHeld ? 1 : 0;
  ratio = 0.71;

  if( |tempX| == |tempY| ) {
    tempX =tempX* ratio;
    tempY =tempY* ratio;
  }
  player.x += tempX;
  player.y += tempY;
}

Я могу решить проблему трения (получить длину вектора движения, уменьшить ее за счет трения, спроецировать обратно с тем же соотношением x: y), однако я не могу обернуть голову вокруг достижения maxSpeed.

Я попробовал решение, не позволяющее игроку вообще ходить, когда скорость выше maxSpeed, но это нарушает пункт 4. Кроме того, у него был неприятный побочный эффект: когда вы двигались на MaxSpeed ​​влево и начинали нажимать вниз, движение направление не изменилось или почти не изменилось.

Затем я начал думать о многочисленных продуктах, различиях и других вещах с векторами, но в основном я не мог больше следить за ними или столкнулся с ранними проблемами.

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

Спасибо за вашу помощь!


2d
person Szoltomi    schedule 31.05.2011    source источник


Ответы (3)


Извините, что не позволил вам думать об этом больше суток, но мне удалось решить эту проблему, и это не две строчки кода. (Хотя все равно спасибо всем за идеи)

Поскольку я ленив и устал, я не буду менять его на псевдокод, кроме следующих двух методов:

updateGame(){
  player.walk();
  player.move();
}
player.move(){
  player.x += player.speedX
  player.y += player.speedY
}  

И код (java):

public void walk() {
    float tempX = 0;
    float tempY = 0;
    float accelX;
    float accelY;
    float nextSpeedX;
    float nextSpeedY;
    float nextSpeed;
    float speed;
    tempX += walkLeft ? -1 : 0;
    tempX += walkRight ? 1 : 0;
    tempY += walkUp ? -1 : 0;
    tempY += walkDown ? 1 : 0;

    if (Math.abs(tempX) == Math.abs(tempY)) {
        tempX = (float) tempX * rat;
        tempY = (float) tempY * rat;
    }

    accelX = tempX * (float) runSpeed;
    accelY = tempY * (float) runSpeed;
    speed = (float) Math.sqrt(speedX * speedX + speedY * speedY);
    nextSpeedX = speedX + accelX;
    nextSpeedY = speedY + accelY;
    nextSpeed = (float) Math.sqrt(nextSpeedX * nextSpeedX + nextSpeedY * nextSpeedY);

    if (nextSpeed > maxSpeed) { //can't accelerate by running
        if (nextSpeed > speed) {  //wants to accelerate
            if (speed > maxSpeed) {  //the current speed is larger than maximum.
                float diff = (float)(speed / nextSpeed);
                float greenX = nextSpeedX*diff;
                float greenY = nextSpeedY*diff;
                accelX = greenX-speedX;
                accelY = greenY-speedY;
            } else { //speed <= maxspeed
                float diff = (float)(maxSpeed / nextSpeed);
                float greenX = nextSpeedX*diff;
                float greenY = nextSpeedY*diff;
                accelX = greenX-speedX;
                accelY = greenY-speedY;
            }
        } else { //wants to slow! allow it!
            //acceleration doesn't need to be changed
        }
    } else { //no problem, allow it!
        //acceleration doesn't need to be changed
    }
    speedX += accelX;
    speedY += accelY;
}

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

person Szoltomi    schedule 01.06.2011

Добавьте в низ:

 If (|tempX|>maxSpeed)
   tempX=maxSpeed*sign(tempX);

 If (|tempY|>maxSpeed)
   tempY=maxSpeed*sign(tempX);

Где int sign (x) {if x ‹0 return -1; }

person Liran Orevi    schedule 31.05.2011
comment
Но если вы идете, например. вниз + влево, ваша скорость в этом диагональном направлении будет sqrt (2 * maxspeed * maxspeed) = 1,41. Это в некоторой степени относится к пункту 2, я мог бы написать этого уборщика. - person Szoltomi; 31.05.2011
comment
@Szoltomi: Мне кажется, что-то вроде If (|tempX, tempY| > maxSpeed) { tempX = sign(tempX)*maxSpeed*ratio; tempY=sign(tempY)*maxSpeed*ratio; } подойдет. - person JAB; 31.05.2011
comment
Спасибо, я думаю, это быстрее закодировать, чем проверять теорию на ошибки. Скоро сообщу о результате. - person Szoltomi; 31.05.2011
comment
Что возвращает знак (x), когда (x ›= 0)? А также баги: запускается игроком с астрономической скоростью. У меня также есть другие переменные, которые я должен включить, так что это не очень простая вставка кода: P Также: мой динамический генератор мира работает астрономически быстро - person Szoltomi; 31.05.2011
comment
@Szoltomi, наверное, 1, я думаю, код должен быть: int sign (x) {return x ‹0? -1: 1; } - person Edison Gustavo Muenz; 31.05.2011
comment
@Edison: Да, я просто использовал sign(x), чтобы указать, что нужно учитывать знак tempX / tempY. - person JAB; 31.05.2011
comment
Извините, мне не удалось заставить ваше решение работать. Не могли бы вы объяснить, что думаете? С другой стороны, я сделал другое решение, которое, похоже, сработало. Сделал большой пояснительный ответ, но решил провести заключительный тест перед публикацией. И увидел, что это нарушает вторую часть части 3. AAARGH. вырывает волосы. Вернуться к доске для рисования... - person Szoltomi; 31.05.2011

Я бы предложил связать определенную скорость с игроком в дополнение к положению и проверять эту скорость только на maxSpeed ​​при нажатии клавиш со стрелками.

Если скорость ДЕЙСТВИТЕЛЬНО превышает maxSpeed, но при этом нажимаются клавиши со стрелками с противоположной стороны, то вы должны оставить текущую скорость выше maxSpeed ​​и просто применить отрицательное ускорение.

person JAB    schedule 31.05.2011
comment
Псевдокод сильно упрощен, у меня действительно есть отдельные переменные speedX и Y, но я их отредактировал. Однако с вашим предложением я столкнулся с проблемой, которую я уже написал: если вы переедете, например. left, и начните удерживать left + down, вы не разгонитесь. - person Szoltomi; 31.05.2011
comment
@Szoltomi: В таком случае вы должны замедлить движение влево, поскольку движение вниз ускоряется. Это хорошо сочетается с вашим вторым условием. - person JAB; 01.06.2011