dota2rpg自定义按键绑定及WASD移动的实现

本文已经过时,最新的做法请参考 DOTA2按键绑定的最新方法 - 简书 (jianshu.com)

本文的目的是在DOTA2自定义游戏中实现一个WASD控制的8方向移动,如果你想实现的是4方向的,稍微修改一点点代码也可以做到。
通过本文,你也可以了解到DOTA2自定义按键的流程。
本文的所有代码你都可以在我开源的Endless Dungeon项目中找到

一、声明按键绑定

按键绑定需要写在 game/dota_addons/你的项目文件夹/addon_info.txt中(完整代码

    "Default_Keys"
    {
        "01"
        {
            "Key"       "D"
            "Command"   "+PlayerMoveRight"
            "Name"      "PlayerMoveRight"
        }
        "02"
        {
            "Key"       "W"
            "Command"   "+PlayerMoveUp"
            "Name"      "PlayerMoveUp"
        }
        "03"
        {
            "Key"       "S"
            "Command"   "+PlayerMoveDown"
            "Name"      "PlayerMoveDown"
        }
        "04"
        {
            "Key"       "A"
            "Command"   "+PlayerMoveLeft"
            "Name"      "PlayerMoveLeft"
        }
}

就完成了按键的声明。

命令的回调声明则是在content/dota_addons/你的项目名称/panorama/scripts/custom_game/key_binding.js文件中(完整代码
其中,要注意的是+代表按键按下,-代表按键抬起。

function WrapFunction(name) {
    return function() {
        Game[name]();
    };
}

(function() {
    Game.AddCommand( "+PlayerMoveUp", WrapFunction("PlayerMoveUp"), "", 0 );
    Game.AddCommand( "+PlayerMoveDown", WrapFunction("PlayerMoveDown"), "", 0 );
    Game.AddCommand( "+PlayerMoveLeft", WrapFunction("PlayerMoveLeft"), "", 0 );
    Game.AddCommand( "+PlayerMoveRight", WrapFunction("PlayerMoveRight"), "", 0 );
    Game.AddCommand( "-PlayerMoveUp", WrapFunction("PlayerMoveUp_End"), "", 0 );
    Game.AddCommand( "-PlayerMoveDown", WrapFunction("PlayerMoveDown_End"), "", 0 );
    Game.AddCommand( "-PlayerMoveLeft", WrapFunction("PlayerMoveLeft_End"), "", 0 );
    Game.AddCommand( "-PlayerMoveRight", WrapFunction("PlayerMoveRight_End"), "", 0 );
})();

这里需要注意的问题是,这个js文件,你最好一次性写完之后,只在custom_ui_menifest.xml里面引用他,这也是为什么需要用WrapFunction方法来把回调放在其他的文件中,这样如果你如果修改回调方法并保存js文件后,引擎重新运行js文件就不会运行到Game.AddCommand,否则,命令绑定就会失效导致按键就会无响应(这是一个已知的BUG,很影响debug的效率)。

二、js的命令回调

在任意被引用的js文件中写明命令的回调如下:

Game.PlayerMoveUp = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_start_move_up", {})
};
Game.PlayerMoveDown = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_start_move_down", {})
};
Game.PlayerMoveLeft = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_start_move_left", {})
};
Game.PlayerMoveRight = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_start_move_right", {})
};

Game.PlayerMoveUp_End = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_end_move_up", {})
};
Game.PlayerMoveDown_End = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_end_move_down", {})
};
Game.PlayerMoveLeft_End = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_end_move_left", {})
};
Game.PlayerMoveRight_End = function() {
    GameEvents.SendCustomGameEventToServer("ed_player_end_move_right", {})
};

三、服务器端的事件响应

服务器在收到ed_player_start_move_up之类的事件之后,做出具体的响应,代码如下,具体说明见注释

function CEDGameMode:_RegisterCustomGameEventListeners()
    -- 注册移动的事件响应,上下左右的开始和结束(按下即开始,松开即结束)
    CustomGameEventManager:RegisterListener("ed_player_start_move_up", function(_, keys)
        self:On_ed_player_start_move_up(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_start_move_down", function(_, keys)
        self:On_ed_player_start_move_down(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_start_move_left", function(_, keys)
        self:On_ed_player_start_move_left(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_start_move_right", function(_, keys)
        self:On_ed_player_start_move_right(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_end_move_up", function(_, keys)
        self:On_ed_player_end_move_up(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_end_move_down", function(_, keys)
        self:On_ed_player_end_move_down(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_end_move_left", function(_, keys)
        self:On_ed_player_end_move_left(keys)
    end)
    CustomGameEventManager:RegisterListener("ed_player_end_move_right", function(_, keys)
        self:On_ed_player_end_move_right(keys)
    end)
end

-- 上下左右四个方向
local upVector    = Vector(0, 1, 0)
local downVector  = Vector(0, - 1, 0)
local leftVector  = Vector(- 1, 0, 0)
local rightVector = Vector(1, 0, 0)

-- 核心的控制上下左右移动的循环
local function createMovingTimer(hero)
    if hero.m_MovingTimer then
        return
    end
    
    -- 创建按键计时器,Timer函数需要https://github.com/XavierCHN/EndlessDungeon/blob/master/game/ed/scripts/vscripts/utils/funcs.lua
    -- 中的Timer函数    
    hero.m_MovingTimer = Timer(function()
        if not (IsValidEntity(hero) and hero:IsAlive()) then
            hero.bMovingUp    = false
            hero.bMovingDown  = false
            hero.bMovingLeft  = false
            hero.bMovingRight = false
            return 0.03
        end

        local movingVector = Vector(0, 0, 0)

        -- 根据玩家英雄当前上下左右的状态,计算玩家当前应该移动的方向
        -- 如按下A则应当向左移动
        -- 同时按AD应当不移动
        -- 同时按WD应当向右上方移动
        if hero.bMovingUp then
            movingVector = movingVector + upVector
        end
        if hero.bMovingDown then
            movingVector = movingVector + downVector
        end
        if hero.bMovingLeft then
            movingVector = movingVector + leftVector
        end
        if hero.bMovingRight then
            movingVector = movingVector + rightVector
        end
        movingVector = movingVector:Normalized()

        -- 具体的移动
        if movingVector.x == 0 and movingVector.y == 0 then
            -- 如果x, y均等于0,则不移动
        else
            -- 开始移动
            if not hero:IsStunned() then
                -- 直接设定面向,这样快速交替按AD才会有响应
                hero:SetForwardVector(movingVector)
                -- 往移动的方向移动一小步
                ExecuteOrderFromTable({
                                          UnitIndex = hero:entindex(),
                                          OrderType = DOTA_UNIT_ORDER_MOVE_TO_POSITION,
                                          Position  = hero:GetOrigin() + movingVector * 32
                                      })
            else
                hero:Stop()
            end
        end
        return 0.03
    end)
end

-- 开始向上移动的按键响应,其他方向的与此类似
function CEDGameMode:On_ed_player_start_move_up(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingUp = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    -- 置向上移动为true
    hero.bMovingUp = true
end
function CEDGameMode:On_ed_player_start_move_down(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingDown = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingDown = true
end
function CEDGameMode:On_ed_player_start_move_left(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingLeft = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingLeft = true
end
function CEDGameMode:On_ed_player_start_move_right(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingRight = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingRight = true
end
function CEDGameMode:On_ed_player_end_move_up(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingUp = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingUp = false
end
function CEDGameMode:On_ed_player_end_move_down(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingDown = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingDown = false
end
function CEDGameMode:On_ed_player_end_move_left(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingLeft = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingLeft = false
end
function CEDGameMode:On_ed_player_end_move_right(keys)
    local player = PlayerResource:GetPlayer(keys.PlayerID)
    if not player then
        return
    end
    local hero = player:GetAssignedHero()
    if not hero then
        return
    end

    if not (IsValidEntity(hero) and hero:IsAlive()) then
        hero.bMovingRight = false
        ShowError("ed_hud_error_cannot_move", keys.PlayerID)
        return
    end

    if not hero.m_MovingTimer then
        createMovingTimer(hero)
    end

    hero.bMovingRight = false
end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352