조회 수 2738 추천 수 1 댓글 6
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄

제대로 된 스크립트를 작성하기 위해서

 

RPG MV의 시스템 스크립트 일부를

 

이해할 필요가 있기에 먼저 설명을 좀 하겠습니다.

 

 

 

1. 배틀러가 행동을 시작합니까?

BattleManager.startAction()을 통해 하게 됩니다.

//일반적으로 배틀러의 턴을 실행하는 함수

BattleManager.processTurn = function() {

    var subject = this._subject;

    var action = subject.currentAction();

    if (action) {

        action.prepare();

        if (action.isValid()) {

            this.startAction();

        }

        subject.removeCurrentAction();

    } else {

        subject.onAllActionsEnd();

        this.refreshStatus();

        this._logWindow.displayAutoAffectedStatus(subject);

        this._logWindow.displayCurrentState(subject);

        this._logWindow.displayRegeneration(subject);

        this._subject = this.getNextSubject();

    }

};

//---------------------------------------------------------------------------------

//강제적인 행동에 의한 배틀러의 행동을 실행하는 함수

BattleManager.processForcedAction = function() {

    if (this._actionForcedBattler) {

        this._subject = this._actionForcedBattler;

        this._actionForcedBattler = null;

        this.startAction();

        this._subject.removeCurrentAction();

    }

};

//---------------------------------------------------------------------------------

//배틀러의 실제적인 행동 실행 함수

BattleManager.startAction = function() {

    var subject = this._subject;

    var action = subject.currentAction();

    var targets = action.makeTargets();

    this._phase = 'action';

    this._action = action;

    this._targets = targets;

    subject.useItem(action.item());

    this._action.applyGlobal();

    this.refreshStatus();

    this._logWindow.startAction(subject, action, targets);

};

 

 

 

2. 행동하는 배틀러는 누구입니까?

subject = this._subject;

this._subejct 가 가르키는 actor 입니다.

 

this._subject = this.getNextSubject();

BattleManager.getNextSubject = function() {

    for ( ; ; ) {

        var battler = this._actionBattlers.shift();

        if (!battler) {

            return null;

        }

        if (battler.isBattleMember() && battler.isAlive()) {

            return battler;

        }

    }

};

또는

 

this._subject = this._actionForcedBattler;

 

this._actionForcedBattler 에 대한 데이터의 흐름

 

 

1) Game_Interpreter의 강제적 행동 판정

// Force Action

Game_Interpreter.prototype.command339 = function() {

    this.iterateBattler(this._params[0], this._params[1], function(battler) {

        if (!battler.isDeathStateAffected()) {

            battler.forceAction(this._params[2], this._params[3]);

            BattleManager.forceAction(battler);

            this.setWaitMode('action');

        }

    }.bind(this));

    return true;

};

 

 

2) BattleManager의 강제적 행동 설정

BattleManager.forceAction = function(battler) {

    this._actionForcedBattler = battler;

    var index = this._actionBattlers.indexOf(battler);

    if (index >= 0) {

        this._actionBattlers.splice(index, 1);

    }

};

 

 

 

3. 배틀러는 어떤 행동을 하게 됩니까?

action = subejct.currentAction();

subejct.currentAction() 에 설정되어 있는 action 을 하게 됩니다.

Game_Battler.prototype.currentAction = function() {

    return this._actions[0];

};

 

 

 

4. 행동의 대상은 누가 됩니까?

targets = action.makeTargets();

targets 로 지정된 대상들입니다.

Game_Action.prototype.makeTargets = function() {

    var targets = [];

    if (!this._forcing && this.subject().isConfused()) {

        targets = [this.confusionTarget()];

    } else if (this.isForOpponent()) {

        targets = this.targetsForOpponents();

    } else if (this.isForFriend()) {

        targets = this.targetsForFriends();

    }

    return this.repeatTargets(targets);

};


Game_Action.prototype.repeatTargets = function(targets) {

    var repeatedTargets = [];

    var repeats = this.numRepeats();

    for (var i = 0; i < targets.length; i++) {

        var target = targets[i];

        if (target) {

            for (var j = 0; j < repeats; j++) {

                repeatedTargets.push(target);

            }

        }

    }

    return repeatedTargets;

};

 

4-1. 행동의 대상을 결정하는 함수들

Game_Action.prototype.confusionTarget = function() {

    switch (this.subject().confusionLevel()) {

    case 1:

        return this.opponentsUnit().randomTarget();

    case 2:

        if (Math.randomInt(2) === 0) {

            return this.opponentsUnit().randomTarget();

        }

        return this.friendsUnit().randomTarget();

    default:

        return this.friendsUnit().randomTarget();

    }

};


Game_Action.prototype.targetsForOpponents = function() {

    var targets = [];

    var unit = this.opponentsUnit();

    if (this.isForRandom()) {

        for (var i = 0; i < this.numTargets(); i++) {

            targets.push(unit.randomTarget());

        }

    } else if (this.isForOne()) {

        if (this._targetIndex < 0) {

            targets.push(unit.randomTarget());

        } else {

            targets.push(unit.smoothTarget(this._targetIndex));

        }

    } else {

        targets = unit.aliveMembers();

    }

    return targets;

};


Game_Action.prototype.targetsForFriends = function() {

    var targets = [];

    var unit = this.friendsUnit();

    if (this.isForUser()) {

        return [this.subject()];

    } else if (this.isForDeadFriend()) {

        if (this.isForOne()) {

            targets.push(unit.smoothDeadTarget(this._targetIndex));

        } else {

            targets = unit.deadMembers();

        }

    } else if (this.isForOne()) {

        if (this._targetIndex < 0) {

            targets.push(unit.randomTarget());

        } else {

            targets.push(unit.smoothTarget(this._targetIndex));

        }

    } else {

        targets = unit.aliveMembers();

    }

    return targets;

};

 

[ 생략 - BattleManager에 _phase / _action / _targets 데이터 지정해주기 ]

 

 

 

5. 배틀러가 아이템을 사용합니까?

: subject.useItem(action.item());

action.item()을 통해 알려주는 아이템을 사용합니다.

Game_Battler.prototype.useItem = function(item) {

    if (DataManager.isSkill(item)) {

        this.paySkillCost(item);

    } else if (DataManager.isItem(item)) {

        this.consumeItem(item);

    }

};

 

 

 

6. 사용한 아이템의 효과를 적용해줍니다.

Game_Action.prototype.applyGlobal = function() {

    this.item().effects.forEach(function(effect) {

        if (effect.code === Game_Action.EFFECT_COMMON_EVENT) {

            $gameTemp.reserveCommonEvent(effect.dataId);

        }

    }, this);

};

 

 

7. BattleManager._phase 에 따른 실행 함수모음

 

실제로 상황에 따른 행동을 실행하는 로직들입니다.

//Scene_Battle부분은 배틀러의 행동을
//결정하는 행동선택 메뉴가 나오는 부분입니다.

//Scene_Battle을 통해서 행동을 결정하기에
//간략하게 넣었습니다.

//어느 곳에서부터 온 코드들인지는
//직접 찾아보시는 것이 도움이됩니다.


Scene_Battle.prototype.updateBattleProcess = function() {

    if (!this.isAnyInputWindowActive() || BattleManager.isAborting() ||

            BattleManager.isBattleEnd()) {

        BattleManager.update();

        this.changeInputWindow();

    }

};


BattleManager.update = function() {

    if (!this.isBusy() && !this.updateEvent()) {

        switch (this._phase) {

            case 'start':

                this.startInput();

            break;

            case 'turn':

                this.updateTurn();

            break;

            case 'action':

                this.updateAction();

            break;

            case 'turnEnd':

                this.updateTurnEnd();

            break;

            case 'battleEnd':

                this.updateBattleEnd();

            break;

        }

    }
};


BattleManager.updateAction = function() {

    var target = this._targets.shift();

    if (target) {

        this.invokeAction(this._subject, target);

    } else {

        this.endAction();

    }

};


BattleManager.endAction = function() {

    this._logWindow.endAction(this._subject);

    this._phase = 'turn';

};


//------------------------------------------------------------------------


BattleManager.invokeAction = function(subject, target) {

    this._logWindow.push('pushBaseLine');

    if (Math.random() < this._action.itemCnt(target)) {

        this.invokeCounterAttack(subject, target);

    } else if (Math.random() < this._action.itemMrf(target)) {

        this.invokeMagicReflection(subject, target);

    } else {

        this.invokeNormalAction(subject, target);

    }

    subject.setLastTarget(target);

    this._logWindow.push('popBaseLine');

    this.refreshStatus();

};


BattleManager.invokeNormalAction = function(subject, target) {

    var realTarget = this.applySubstitute(target);

    this._action.apply(realTarget);

    this._logWindow.displayActionResults(subject, realTarget);

};


BattleManager.invokeCounterAttack = function(subject, target) {

    var action = new Game_Action(target);

    action.setAttack();

    action.apply(subject);

    this._logWindow.displayCounter(target);

    this._logWindow.displayActionResults(subject, subject);

};


BattleManager.invokeMagicReflection = function(subject, target) {

    this._logWindow.displayReflection(target);

    this._action.apply(subject);

    this._logWindow.displayActionResults(subject, subject);

};


Game_Action.prototype.apply = function(target) {

    var result = target.result();

    this.subject().clearResult();

    result.clear();

    result.used = this.testApply(target);

    result.missed = (result.used && Math.random() >= this.itemHit(target));

    result.evaded = (!result.missed && Math.random() < this.itemEva(target));

    result.physical = this.isPhysical();

    result.drain = this.isDrain();

    if (result.isHit()) {

        if (this.item().damage.type > 0) {

            result.critical = (Math.random() < this.itemCri(target));

            var value = this.makeDamageValue(target, result.critical);

            this.executeDamage(target, value);

        }

        this.item().effects.forEach(function(effect) {

            this.applyItemEffect(target, effect);

        }, this);

        this.applyItemUserEffect(target);

    }

};

 

 

 

결론적으로 정리해드리자면...

 

target 은 행동의 대상이 되는 actor이고

 

subject는 행동의 주체가 되는 actor라는 것입니다.

 

 

질문에 들어간 다음의 스크립트에서

 

 

$gameActors.actor(7).mat *35.8 - $gameTroop.members()[0].mdf * 2

 

 

다음의 변수를 공격의 주체인

 

subject 변수가 가르키게 됩니다.

 

$gameActors.actor(7)

 

 

다음의 변수를 공격의 대상인

 

target 변수가 가르기케 됩니다.

 

$gameTroop.members()[0]

 

 

 

공격의 대상인 $gameTroop.members()[0]

 

결과적으로 크리티컬 상태가 true인지

 

조건문으로 체크해주시면 되겠습니다.

 

 

//actor 의 데이터를 세팅하는 함수들

Game_Actor.prototype.initialize = function(actorId) {

    Game_Battler.prototype.initialize.call(this);

    this.setup(actorId);

};

Game_Actor.prototype.initMembers = function() {

    Game_Battler.prototype.initMembers.call(this);

    this._actorId = 0;

    this._name = '';

    this._nickname = '';

    this._classId = 0;

    this._level = 0;

    this._characterName = '';

    this._characterIndex = 0;

    this._faceName = '';

    this._faceIndex = 0;

    this._battlerName = '';

    this._exp = {};

    this._skills = [];

    this._equips = [];

    this._actionInputIndex = 0;

    this._lastMenuSkill = new Game_Item();

    this._lastBattleSkill  = new Game_Item();

    this._lastCommandSymbol = '';

};

Game_Actor.prototype.setup = function(actorId) {

    var actor = $dataActors[actorId];

    this._actorId = actorId;

    this._name = actor.name;

    this._nickname = actor.nickname;

    this._profile = actor.profile;

    this._classId = actor.classId;

    this._level = actor.initialLevel;

    this.initImages();

    this.initExp();

    this.initSkills();

    this.initEquips(actor.equips);

    this.clearParamPlus();

    this.recoverAll();

};

//._actorId는 전역변수 $dataActors에 있는 actor 데이터에
//접근하는데 쓸 수 있고, 액터간의 키 데이터가 됩니다.

//1. 현재 행동의 주체가 되는 actor의 ._actorId 와
//행동의 대상이 되는 actor의 _actorId를 사용하여
//데이터의 일치성을 검사해주시면 됩니다.

//2. 행동의 대상이 되는 actor의 .result().critical 의
//boolean값이 true인가 false인가를 체크해주시면 됩니다.

//3. 연산자는 모든 조건이 만족되어야지만 실행을 해야하기에
//&& 연산자로 모두 묶어주셔야 합니다.


BattleManager.set_target = function (actor){
    BattleManager._target = actor;
};

function find_target_actorId(actor){
    return BattleManager._target._actorId === actor._actorId;
};

//---------------------------------------------------------------------------------

var subject = $gameActors.actor(7);
var target;

BattleManager.set_target($gameTroop.members()[0]);
target = BattleManager._targets.find(find_target_actorId);

if(BattleManager._subject._actorId === subject._actorId
&& typeof target === "object"
&& target.result().critical){
    //데미지 처리는 알아서 하실 수 있을 것이라 봅니다.
}

 

 

결과부분의 2개 함수추가는 하지 않으시면

 

제대로 데이터를 걸러 낼 수 없습니다.

 

 

하나는 BattleManager에서 전역변수화해서

 

BattleManager._target 으로 접근할 수 있도록 합니다.

 

 

다른 하나는 BattleManager._targets 배열 변수에 있는

 

actor 데이터에서 ._actorId 가 BattleManager._target._actorId와

 

같으면 해당 actor의 데이터를 target변수가 가르키게 하는 용도입니다.

 

 

 

 

ps. 혹시나 해보시고 제대로 안되시는 부분이

 

있다면 과감히 질문해주시기 바랍니다.

 

실제 게임을 실행하면서 스크립팅을 한 것이

 

아니라 RPG MV 시스템 스크립트를 뜯어보면서

 

작성한 것이라 실제 실행시에 문제가

 

있을 수 있으니 참고하시기 바랍니다.

?
  • ?
    케에데 2017.01.31 20:46 Files첨부 (1)

    TypeError
    Cannot read property 'list' of undefined 라고 오류가 뜨네요.

    그리고...

    123.jpg

    스크립트 적는 칸이 저렇게 작아서 한줄로 적어야 되는데 처리상의 문제는 없는건가요?

     

  • profile
    lklslel 2017.01.31 22:46

    저는 메뉴얼로 파일에 작성하여 적용하는 타입으로 작성해드렸습니다.

    추가되는 2개의 함수는 플러그인의 형식으로 적용해주시지 않으면

    제대로 작동하지 않거나 참조할 때에 변수에서부터 에러가 납니다.


    그 이유는 케에데님께서 붙여넣으신 에러에서부터 찾아볼 수 있습니다.

    TypeError
    Cannot read property 'list' of undefined

    이 에러의 뜻은

    정의되지 않은 변수(un-defined variable)의 'list' 속서을 읽을 수 없습니다.

    입니다.


    아 오타가 중간에 났습니다.



    var subject = $gameActors.actor(7);
    var target;

    BattleManager.set_target($gameTroop.members()[0]);
    target = BattleManager._target.find(find_target_actorId);

    if(BattleManager._subject._actorId === subject._actorId
    && typeof target === "object"
    && target.result().critical){
    //데미지 처리는 알아서 하실 수 있을 것이라 봅니다.
    }

    부분에서


    target = BattleManager._target.find(find_target_actorId);

    부분을 다음과 같이 수정했습니다.

    target = BattleManager._targets.find(find_target_actorId);

     

     

    제가 제대로 살펴보지 않았습니다.

     

    죄송합니다. 's' 한 글자 오타때문에

     

    Array.find() 함수의 실행부분에서 오류가 났습니다.

  • ?
    마니아 2017.01.31 20:48
    스크립트는 별도의 스크립트 에디터를 실행해서 수정해야...........
  • ?
    케에데 2017.01.31 21:05
    그럼 저렇게 바로 변수에다가는 스크립트를 못쓰는건가요?
    그리고 스크립트 에디터가 MV 자체에 별도로 존재하는 기능인가요? VX나 VXA 때하고는 달리 스크립트 에디터는없고 플러그인 불러오는 기능밖에 없는거 같던데...
  • profile
    lklslel 2017.01.31 22:52
    해당 에러는 변수형 에러입니다.


    Array 형의 데이터가 아닌데 Array 형의 데이터에서만

    사용할 수 있는 .find()함수를 사용했기에 발생한 에러입니다.

    실제로 게임을 실행하면서 테스트를 했다면 에러가 없는

    스크립트를 제공해드릴 수 있었을 텐데 말이죠...
  • profile
    러닝은빛 2017.02.01 13:26

    Yanfly님이 만든 배틀 플러그인들은 데이터베이스의 메모(note)란에 관련 스크립트를 작성할 수 있게 되어있습니다. 예를 들면, 데미지 코어로는 데미지 값을 변경할 수 있습니다. 물론 이런 루나틱 모드를 사용하려면 자바스크립트를 할 줄 알아야 합니다.

     

    <damage formula>
    if (b.result().critical) {
      value = (a.mat *35.8 - b.mdf * 2)* 2;
    } else {
      value = a.mat *35.8 - b.mdf * 2;
    }
    </damage formula>


List of Articles
번호 제목 글쓴이 날짜 조회 수
8648 간단하게 몇가지 질문 s제이노s 2005.05.29 248
8647 rpg2000 변수/x 대화창에 나타내는 법 KBOY∮ 2005.05.29 588
8646 rpg2000 변수/x 대화창에 나타내는 법 오예스 2005.05.29 727
8645 gamemaker 에 대해서..[겜메 하시는분답변점] KBOY∮ 2005.05.29 223
8644 rpg 2003 문장의 표시 Norid 2005.05.29 721
8643 정확한 개요 KBOY∮ 2005.05.29 221
8642 정확한 개요 덩키동크 2005.05.29 204
8641 정확한 개요 s제이노s 2005.05.29 253
8640 저기요! 질문요 KBOY∮ 2005.05.29 260
8639 저기요! 질문요 #샤프트#엘프 2005.05.29 325
8638 gamemaker 에 대해서..[겜메 하시는분답변점] 이더플 2005.05.30 385
8637 여기다 질문하는거맞나? KBOY∮ 2005.05.30 229
8636 여기다 질문하는거맞나? KBOY∮ 2005.05.30 224
8635 초보자에게는 조금 어려울 수 있는.. 타다기 2005.05.30 209
8634 여기다 질문하는거맞나? Kensin 2005.05.30 304
8633 오프닝 문제 입니다!! メIllusion。 2005.05.30 208
8632 오프닝 문제 입니다!! 人生은 鸞誌라 2005.05.30 421
8631 배우려는 학생으로서 질문하나하겠습니다. 하앙e 2005.05.30 371
8630 스크립트 좋은것 구합니다. 人生은 鸞誌라 2005.05.30 409
8629 궁금사항인데물겠습니다... メIllusion。 2005.05.30 199
Board Pagination Prev 1 ... 5 6 7 8 9 10 11 12 13 14 ... 442 Next
/ 442






[개인정보취급방침] | [이용약관] | [제휴문의] | [후원창구] | [인디사이드연혁]

Copyright © 1999 - 2016 INdiSide.com/(주)씨엘쓰리디 All Rights Reserved.
인디사이드 운영자 : 천무(이지선) | kernys(김원배) | 사신지(김병국)