查看: 646|回复: 4

TWs小技巧之——修改源代码以获得更好的游戏体验

[复制链接]

该用户从未签到

128

积分

112

子弹

14

武士刀

Lv.3 霰弹

Rank: 2

积分
128

第二届DDN高级组第三届DDN闯关竞速

发表于 2018-3-2 22:25:34 | 显示全部楼层 |阅读模式
本帖最后由 Eki 于 2018-3-2 22:35 编辑

相信各位都遇到过在游戏中需要固定鼠标的情况,比如在双重榴弹跳当中为了尽量跳得高一点,需要先让鼠标在竖直向上的方向来一发,然后再让鼠标在竖直向下的方向再来一发。有一种替代案,就是利用游戏的原生指令:
bind z "+toggle cl_mouse_max_distance 2 400"

使得鼠标只有8的方向的朝向,这种方法缺点是容易因为手抖改变朝向。

注意到在游戏中我们可以通过inp_mousesens指令改变鼠标灵敏度,然而可以容许修改到的灵敏度最小值为5。在ddnet的代码项目中全局搜索,在/src/engine/shared/config_variables.h是找到如下一行:

MACRO_CONFIG_INT(InpMousesens, inp_mousesens, 200, 5, 100000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Mouse sensitivity")

而MACRO_CONFIG_INT的定义可以在/src/engine/shared/config.h找到,为:

struct CConfiguration
{
    #define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Save,Desc) int m_##Name;
    #define MACRO_CONFIG_STR(Name,ScriptName,Len,Def,Save,Desc) char m_##Name[Len]; // Flawfinder: ignore
    #include "config_variables.h"
    #undef MACRO_CONFIG_INT
    #undef MACRO_CONFIG_STR
};

根据变量名我们可以猜测inp_mousesens是个整数型数值,其取值范围在5到100000之间。把5改名0,重新编译项目:

mkdir bulid && cd bulid && cmake && make //linux下只需短短一行命令即可

然后在bulid目录就会出现游戏的二进制包,进入游戏,发现inp_mousesens 0命令已经可以成功地让鼠标静止不动了。

为了更好地使用这个功能,我建议再bind一个快捷键:

bind q toggle inp_mousesens 0 200

即可用q键控制这个功能的开关。


欢迎来到Teeworlds 中国社区!

该用户从未签到

128

积分

112

子弹

14

武士刀

Lv.3 霰弹

Rank: 2

积分
128

第二届DDN高级组第三届DDN闯关竞速

 楼主 发表于 2018-3-2 22:35:50 | 显示全部楼层
//仅供学习交流

在除了DDrace/Race模式的其它模式里,游戏的缩放功能都无法使用,然而并不是这个功能在其它模式就自动失效了,而是DDNet官方制作组特意限制了这个功能。按照同样的分析思路,我们可以在DDnet项目里搜索一下zoom-或者zoom+命令的所在位置。然后我们可以发现缩放功能是在/src/game/client/components/camera.cpp文件里实现的,把相应的判断语句去掉即可解除这一限制。具体代码我就不贴了。
欢迎来到Teeworlds 中国社区!
回复 支持 反对

使用道具 举报

该用户从未签到

128

积分

112

子弹

14

武士刀

Lv.3 霰弹

Rank: 2

积分
128

第二届DDN高级组第三届DDN闯关竞速

 楼主 发表于 2018-3-3 11:02:02 | 显示全部楼层
在前面我们所做的都是修改已有的游戏指令,那如何定义一个新的游戏指令呢。

比如我想自定义一个让鼠标水平向右移动的指令mouse_move_right:

首先根据前面的搜索我们可以发现定义新指令的命令为Console()->Register(...)

全局搜索::Register(,可以找到这个函数定义的位置,从而根据参数名称判断各个参数的含义。

void CConsole::Register(const char *pName, const char *pParams, int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)

为了避免麻烦,我们把新的mouse_move_right指令定义在 已经定义了多条指令的/src/game/client/components/controls.cpp文件。

{ static CInputState s_State = {this, &m_InputData[0].m_Fire, &m_InputData[1].m_Fire}; Console()->Register("mouse_move_right", "", CFGFLAG_CLIENT, MouseMoveRight, (void *)&s_State, "mouse move right");}

CInputState是一个结构体,其定义为:

struct CInputState
{
    CControls *m_pControls;
    int *m_pVariable1;
    int *m_pVariable2;
};

虽然在mouse_move_right指令里用不到结构体中的第二和第三个变量,但是同样为了避免多余的麻烦我们让定义的保持格式一致。

在指令的定义里MouseMoveRight对应着形参FCommandCallback pfnFunc,即让游戏里的mouse_move_right指令对应MouseMoveRight函数。

最后在void CControls::OnConsoleInit()的上方定义MouseMoveRight函数。


static void MouseMoveRight(IConsole::IResult *pResult, void *pUserData){

    CInputState *pState = (CInputState *)pUserData;

    pState->m_pControls->m_MousePos[g_Config.m_ClDummy].x += 1;
    pState->m_pControls->m_MousePos[g_Config.m_ClDummy].y = 0;

    std::cout << pState->m_pControls->m_MousePos[g_Config.m_ClDummy].x << ", " << pState->m_pControls->m_MousePos[g_Config.m_ClDummy].y << std::endl;
}


编译运行,在游戏里的F1控制台输入mouse_move_right指令,我们会发现鼠标和预想的一样向右边移动。

为了使用方便,建议bind一个快捷键:

bind right mouse_move_right
欢迎来到Teeworlds 中国社区!
回复 支持 反对

使用道具 举报

该用户从未签到

128

积分

112

子弹

14

武士刀

Lv.3 霰弹

Rank: 2

积分
128

第二届DDN高级组第三届DDN闯关竞速

 楼主 发表于 2018-3-6 03:59:37 | 显示全部楼层
如何定义一个新的游戏设置。

游戏设置与游戏指令不一样,游戏指令本身通过Console()->Register(...)绑定游戏内部的某个函数,从而在每次执行该指令的时候调用该函数。比如+weapon1,+fire还有我们之前定义的mouse_move_right。

而游戏设置则基本上是一个全局变量,可以通过"g_Config.变量名"调用,正如顶楼所提到的,其定义在src/engine/shared/config.h文件可以找到。变量名被宏定义为"m_自定义的变量名"的形式。

在这里我们以teetower功能作为说明:

teetower是指一个tee正好叠在另一个tee的头上的一种状态,显得十分的滑稽(和pro?),是玩家们在游戏中无聊的等待过程中自创的一个小游戏,我们将用定义一个新的游戏设置引入这个功能。

首先在文件/src/engine/shared/config_variables.h新增一行全局变量的定义:
MACRO_CONFIG_INT(clTeeTower, cl_tee_tower, 0, 0, 1, CFGFLAG_CLIENT, "Wether use teetower")
在这之后我们就能通过g_Config.m_clTeeTower调用这个全局变量。

然后我们在/src/game/client/components/controls.cpp里的int CControls::SnapInput(int *pData)函数里改变一下程序的逻辑。SnapInput是游戏每一个tick(20ms)都会自动调用一次的函数,我们需要改动一下里面设置人物移动方向的逻辑。

                // set direction
                m_InputData[g_Config.m_ClDummy].m_Direction = 0;

                if (g_Config.m_clTeeTower){
                        //这里放入你的代码实现
                else {
                        // 这里是原代码
                        if(m_InputDirectionLeft[g_Config.m_ClDummy] && !m_InputDirectionRight[g_Config.m_ClDummy])
                                m_InputData[g_Config.m_ClDummy].m_Direction = -1;
                        if(!m_InputDirectionLeft[g_Config.m_ClDummy] && m_InputDirectionRight[g_Config.m_ClDummy])
                                m_InputData[g_Config.m_ClDummy].m_Direction = 1;
                        l_HookedPlayer = -1;
                        l_TeeTower = false;
                }

具体的代码实现我也不贴出来了,实现好以后编译运行,在游戏中bind一个快捷键:
bind c +toggle cl_tee_tower 1 0
就可以在按下c键的状态下在另一位tee的头上来回滑动而不掉下来。
欢迎来到Teeworlds 中国社区!
回复 支持 反对

使用道具 举报

该用户从未签到

128

积分

112

子弹

14

武士刀

Lv.3 霰弹

Rank: 2

积分
128

第二届DDN高级组第三届DDN闯关竞速

 楼主 发表于 2018-3-6 04:10:23 | 显示全部楼层
温馨提示,如果要访问游戏中的某些变量,比如玩家位置,武器状态等,可以先看源代码中相关的函数实现是怎么访问这些变量的,再用全局搜索大法找到对应的头文件里的类,类的父类,类里面另一个类的指针,类里面的结构体这些乱七八糟的东西的定义,然后就大概知道如何调用相应变量了。另外根据变量名也能大概猜出这些变量的含义。
欢迎来到Teeworlds 中国社区!
回复 支持 反对

使用道具 举报

游客
请先登录
您需要登录后才可以回帖 登录 | 立即加入

本版积分规则

快速回复 返回顶部 返回列表