Rol yapma oyununda savaş sırasının programlanması


13

Bir oyuncunun etrafta dolaştığı ve canavarlarla savaştığı kısa bir "oyun" yazmaya çalışıyorum, ancak savaşın nasıl ele alınacağı hakkında hiçbir fikrim yok.

Örneğin, bir "Savaşçı" ve bir "Trol" var. İkisi birbiriyle nasıl kavga ediyor? Biliyorum ki böyle bir şey yapabilirim

Conan = Warrior.new();
CaveTroll = Troll.new();
Conan.attack(CaveTroll);
CaveTroll.attack(Conan);

Ama oyunun hangi kısmı canavarı kontrol ediyor? Bunlardan biri ölene kadar yukarıdaki diziyi bir döngüye yapıştırır mıyım? Yoksa oyunun "motor" u özellikle savaş ile ilgilenen bir parçaya mı ihtiyaç duyuyor? Yoksa bu, Troll'un yapay zekanın eylemleriyle ilgilenmesi gereken bir yönü mü?

Ayrıca, canavarın yaptığı eylemleri kim / ne belirler? Belki bir Trol bash, tekme, ısırmak, büyü yapmak, iksir içmek, büyülü bir eşya kullanabilir. Oyun motoru, Troll'un hangi eylemi gerçekleştirdiğini belirler mi yoksa Troll sınıfının yönettiği bir şey mi?

Maalesef daha spesifik olamıyorum ama bununla ilgili hangi yöne gideceğime dair bir rehberliğe ihtiyacım var.


güzel! bu sitenin var olduğunu bilmiyordum. sorumu oraya taşımamın bir yolu var mı? ya da sadece orada kesip yapıştırmalı mıyım?

Endişeye gerek yok, bir mod çok yakında hareket etmeli! Veya soruyu buradan silebilir ve Game
Dev'de

@Fendo sorduğum için özür dilerim, ama ne demek istiyorsun? Oyun Dev?
user712092

Yanıtlar:


12

Oyununuzda bir mini oyun olarak bir savaş sırası hayal ediyorum. Güncelleme keneleri (veya dönüş keneleri) bu olayları işleyen bir bileşene yönlendirilir. Bu yaklaşım, savaş sırası mantığını ayrı bir sınıfta toplar ve ana oyun döngünüzü oyun durumları arasında geçişe serbest bırakır.

void gameLoop() {
    while(gameRunning) {
        if (state == EXPLORATION) {
            // Perform actions for when player is simply walking around
            // ...
        }
        else if (state == IN_BATTLE) {
            // Perform actions for when player is in battle
            currentBattle.HandleTurn()
        }
        else if (state == IN_DIALOGUE) {
            // Perform actions for when player is talking with npcs
            // ...
        }
    }

}

Savaş sırası sınıfı şöyle görünür:

class BattleSequence {
    public:
        BattleSequence(Entity player, Entity enemy);
        void HandleTurn();
        bool battleFinished();

    private:
        Entity currentlyAttacking;
        Entity currentlyReceiving;
        bool finished;
}

Troll ve Warrior'ların her ikisi de Entity adlı ortak bir süper sınıftan miras alır. HandleTurn içinde, saldıran varlığın hareket etmesine izin verilir. Bu bir AI düşünme rutinine eşdeğerdir.

void HandleTurn() {
    // Perform turn actions
    currentlyAttacking.fight(currentlyReceiving);

    // Switch sides
    Entity temp = currentlyAttacking;
    currentlyAttacking = currentlyReceiving;
    currentlyReceiving = temp;

    // Battle end condition
    if (currentlyReceiving.isDead() || currentlyAttacking.hasFled()) {
        finished = true;
    }
}

Dövüş yöntemi, işletmenin ne yapacağına karar verir. Bunun iksir içmek veya kaçmak gibi karşıt varlığı içermesi gerekmediğini unutmayın.

Güncelleme: Birden fazla canavarı ve bir oyuncu partisini desteklemek için bir Grup sınıfı tanıtırsınız:

class Group {
    public:
        void fight(Group opponents) {
            // Loop through all group members so everyone gets
            // a shot at the opponents
            for (int i = 0; i < memberCount; i++) {
                Entity attacker = members[i];
                attacker.fight(opponents);
            }
        }

        Entity get(int targetID) {
            // TODO: Bounds checking
            return members[targetID];
        }

        bool isDead() {
            bool dead = true;
            for (int i = 0; i < memberCount; i++) {
                dead = dead && members[i].isDead();
            }
            return dead;
        }

        bool hasFled() {
            bool fled = true;
            for (int i = 0; i < memberCount; i++) {
                fled = fled && members[i].hasFled();
            }
            return fled;
        }

    private:
        Entity[] members;
        int memberCount;
}

Group sınıfı, BattleSequence sınıfındaki tüm Varlık oluşumlarının yerini alacaktır. Seçim ve saldırı, Entity sınıfının kendisi tarafından ele alınacaktır, böylece AI, en iyi eylem yolunu seçerken tüm grubu dikkate alabilir.

class Entity {
    public:
        void fight(Group opponents) {
            // Algorithm for selecting an entity from the group
            // ...
            int targetID = 0; // Or just pick the first one

            Entity target = opponents.get(targetID);

            // Fighting algorithm
            target.applyDamage(10);
        }
}

Bunun sadece bir oyuncu ve bir canavar için çalışacağını varsayıyorum. Yoksa bir oyuncunun birden çok canavara karşı çalışması için bunu güncellemek kolay mı olur?
Harv

Her iki canavar tarafındaki gruplara oyuncu tarafı olarak destek eklemek oldukça kolaydır (sizin durumunuzda, oyuncu grubu sadece bir üye içerir: oyuncu karakteri). Bu senaryonun cevabını güncelledim.
hayalet

1

Dövüşü yöneten özel bir Combat nesnesim olurdu. Oyuncu karakterleri listesi, düşman listesi, şu anki dönüş, savaş alanı vb.Gibi tüm savaş durumunu kapsamaktadır. Savaş daha sonra savaş mantığını yöneten bir güncelleme yöntemine sahip olabilir. Savaş kodunu basit bir döngüye koymak iyi bir fikir değil, çünkü gerçekten hızlı bitiyordu. Normalde zamanlama ve farklı savaş aşamaları olur.

Gerçekleştirilen eylemler için kesinlikle rastgele yapabilirsiniz, ancak tam HP'ye sahip bir canavarın iyileştirici bir büyü yapması çok mantıklı olmaz. Hangi eylemin gerçekleştirileceğini belirlemek için bazı temel mantıklara sahip olmak gerekir. Örneğin, bazı eylemlerin diğerlerinden daha fazla önceliği olabilir (örneğin, trol zamanın% 30'unu başlatır) ve savaşları daha ilginç hale getirmek için diğer koşullar (örneğin, trol HP'nin tam HP'nin% 10'undan az olması durumunda,% 20 vardır. iyileşme büyüsü yapma şansı, aksi takdirde şans% 1). Bu istediğiniz kadar karmaşık olabilir.

Canavar sınıfının hangi eylemi yapacağını seçmesi gerektiğini düşünüyorum, savaş nesnesi canavardan bir eylem istediğini ve canavar bir seçim yaptığını ve ardından uygulamayı sürdürdüğünü düşünüyorum. Bir fikir, canavarlara taktığınız ve her savaş eylemine atanan önceliklere, kategorilere ve koşullara göre olası canavar eylemleri listesinden seçim yapan bir strateji nesnesine sahip olmaktır. Ardından, örneğin savunma becerilerine yönelik saldırılara öncelik veren bir OffensiveStrategy sınıfına ve iyileşme olasılığı daha yüksek olan başka bir CautiousStrategy sınıfına sahip olabilirsiniz. Bir patron mevcut durumuna göre stratejiyi dinamik olarak değiştirebilir.

Son bir şey. Hem oyuncu karakterlerinin hem de canavarların aynı sınıftan miras almasını, aynı sınıfın örneklerini (örneğin aktör veya savaşçı) olmasını veya ortak işlevselliği içine alan ortak bir nesneyi paylaşmak isteyebilirsiniz. Bu, kod çoğaltmayı azaltır ve ayrıca, canavarlar için kodladığınız aynı stratejileri uygulayabilen AI kontrollü NPC'lerin yanınızda olmasına izin verir.


1

Evet, motorunuzda dövüşle ilgilenen özel bir parçanız olması gerekir.

Savaşınızı tam olarak nasıl yaptığınızı bilmiyorum ama oyuncuların oyun dünyasında dolaştıklarını, canavarlarla tanıştıklarını ve savaşın gerçek zamanlı gittiğini varsayacağım. Eğer öyleyse, trolün belirli bir alandaki çevreyi bilmesi gerekir, belki de trolün önünde bir şey görebileceğini tanımlayın (trol bunu işler).

AI hakkında, motorun kendisiyle başa çıkması gerektiğini düşünüyorum, böylece aynı şeyi (ısırık) yapabilen birden fazla düşmanınız olduğunu varsayalım, AI'yı başka bir canavara atayabilirsiniz ve işte gidiyorsunuz!


0

Oynatıcınız ve trolünüz, dünyanızı tanımlayan veri Modeli olarak adlandırdığımız veri kümelerinden başka bir şey değildir. Yaşam, envanter, saldırı yetenekleri, dünya hakkındaki bilgileri bile - hepsi veri modelinden oluşur.

Dünyanızı tanımlayan tüm verileri tutan tek bir ana Model nesnesini saklayın. Zorluk, fizik parametreleri vb. Gibi genel dünya bilgilerini tutacaktır.Ayrıca yukarıda açıkladığım gibi belirli varlıkların verilerinin bir listesini / dizisini de tutacaktır . Bu ana model dünyanızı tanımlamak için birçok alt nesneden oluşabilir. Modelinizin hiçbir yerinde oyun mantığını veya ekran mantığını kontrol eden herhangi bir fonksiyonunuz olmamalıdır; alıcılar tek istisnadır ve yalnızca modelden daha kolay veri almanıza izin vermek için kullanılır (eğer kamu üyeleri hile yapmazsa).

Ardından, bir veya daha fazla "denetleyici" sınıfında işlevler oluşturun; hepsini ana sınıfınızda yardımcı işlevler olarak yazabilirsiniz, ancak bu bir süre sonra biraz büyüyebilir. Bunlara, kuruluşun verileri üzerinde farklı amaçlarla (hareket, saldırı vb.) Hareket etmek için her güncelleme denir. Bu işlevleri bir varlık sınıfının dışında tutmak daha kaynak verimlidir ve varlığınızı neyin tanımladığını öğrendikten sonra, hangi işlevlerin üzerinde işlem yapması gerektiğini otomatik olarak bilirsiniz.

class Main
{

//...members variables...
var model:GameModel = new GameModel();

//...member functions...
function realTimeUpdate() //called x times per second, on a timer.
{
    for each (var entity in model.entities)
    {
        //command processing
        if (entity == player)
            decideActionsFromPlayerInput(entity);
        else //everyone else is your enemy!
            decideActionsThroughDeviousAI(entity);

        act(entity);
    }
}
//OR
function turnBasedUpdate()
{
    if (model.whoseTurn == "player")
    {
        decideActionsFromInput(model.player); //may be some movement or none at all
        act(player);
    }
    else
    {
        var enemy;
        for each (var entity in model.entities)
        {
            if (entity != model.player)
            {
                enemy = entity;
                decideActions(enemy);
                act(enemy);
            }
        }
    }
}

//AND THEN... (common to both turn-based and real-time)
function decideActionsThroughDeviousAI(enemy)
{
    if (distanceBetween(enemy, player) <= enemy.maximumAttackDistance)
        storeAttackCommand(enemy, "kidney punch", model.player);
    else
        storeMoveCommand(player, getVectorFromTo(enemy, model.player));

}

function decideActionsFromPlayerInput(player)
{
    //store commands to your player data based on keyboard input
    if (KeyManager.isKeyDown("A"))
        storeMoveCommand(player, getForwardVector(player));
    if (KeyManager.isKeyDown("space"))
        storeAttackCommand(player, "groin slam", currentlyHighlightedEnemy);
}
function storeAttackCommand(entity, attackType, target)
{
    entity.target = target;

    entity.currentAttack = attackType;
    //OR
    entity.attackQueue.add(attackType);
}
function storeMoveCommand(entity, motionVector)
{
    entity.motionVector = motionVector;
}
function act(entity)
{
    entity.position += entity.motionVector;
    attack(entity.target, entity.currentAttack);
}
}

class GameModel
{
    var entities:Array = []; //or List<Entity> or whatever!
    var player:Entity; //will often also appear in the entity list, above
    var difficultyLevel:int;
    var globalMaxAttackDamage:int;
    var whoseTurn:Boolean; //if turnbased
    //etc.

}

Son bir not, ekran mantığınızı oyun mantığınızdan ayrı tutmanın da yararlı olduğudur. Görüntü mantığı, "Bunu ekranda nerede ve hangi renkte çizerim?" Oyun mantığı, yukarıdaki sahte kodda özetlediğim şey.

(Dev'in notu: Sınıfları kullanırken, bu, tüm yöntemleri ideal olarak vatansız olarak gören fonksiyonel bir programlama yaklaşımını gevşek bir şekilde izler ve korunan durumun neden olduğu hataları en aza indiren temiz bir veri modeli ve işleme yaklaşımına izin verir. Endişelerin açıkça ayrılması hedefi. Bu soruya bakın .)


1
"Dünyanızı tanımlayan tüm verileri tutan tek bir ana Model nesnesini saklayın. Zorluk, fizik parametreleri gibi genel dünya bilgilerini tutacaktır." Zorluk ve fizik parametreleri? Endişelerin giderilmesi hakkında konuşun! -1.

2
@Joe - Ona tüm yapılandırma hiyerarşisini özetlememi ister misiniz? Burada basit tutuyoruz, değil mi? İndirmeden önce düşünürseniz sevinirim.
Mühendis

3
Yazının geri kalanı, V'yi veya normalde C olarak tanınabilen herhangi bir şeyi kapsamaksızın MVC'yi kapsamak için tuhaf bir girişimdir ve MVC'nin ilk etapta oyun programlama için iyi bir tavsiye olduğunu düşünmüyorum. Cevap vermeden önce düşünürseniz sevinirim, ama istediğimizi her zaman elde edemeyiz.

1
@Joe: MVC'nin bir oyun için zor bir seçim olduğuna katılıyorum, ama burada V'nin rolünün açık olduğundan eminim.
Zach Conn

4
@Zach: "FP nihai MVC" gibi iddialar yapıldığında, hiçbir şey açık değildir, ancak posterin hem MVC'yi hem de fonksiyonel programlamayı anlayamaması dışında.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.