搜索
查看: 3515|回复: 5

【分享】浅谈表情插件制作经验

[复制链接]
发表于 2005-9-8 15:35:04 | 显示全部楼层 |阅读模式 来自 广东广州
编写程序是一件很需要技巧的事情,经过写这个表情秀插件,我学到很多东西,下面就把详细的制作步骤告诉大家,希望能够给准备学习写脚本的朋友一点提示。
        以这个插件为例,如果我们把每个表情命令都进行注册,那是相当麻烦的,而且也不利于更新,如果有10个以上的表情,就会有很多菜单,同样的,如果把每个菜单都定义的话,那也是很麻烦,而且代码冗余,所以,我们就要考虑一种更合理的方法来实现我们想要的结果。
        假如,我们把所有的表情文件名称全部保存在一个配置文件里面,例如spr_list.ini,然后通过程序来读取一定数量行数和字符,然后把数据存入到一个二维数组中,再从这个数组中读取内容然后自动生成相应数量的菜单而且自动判断是否需要以多页来显示,呵呵,我们的要求是不是太多了?程序完全可以做到这点。不理解的朋友不要害怕,下面就让我们一步步来完成,然后你会发现,原来这么简单。
        首先,我们必须定义几个常量(就是固定不可更改),程序自动生成的每页最大菜单数量,最大SPR文件数量。
#define MAX_DISPLAY 8//程序自动生成每页最大菜单
这里因为菜单需要more或者back和exit选项,所以只定义8,否则我们就无法选择更多或者返回。
#define MAX_SPRITES 24//定义最大SPR文件数量,这可以改变,随自己喜欢。
        第二步,我们需要几个数组来存取数据。
new get_spr_name[MAX_SPRITES][32]
//定义一个二维数组,包含最大字符串数量为MAX_SPRITES,单个字符串长度32。
new get_spr_precached[MAX_SPRITES]
//定义SPR缓存,这里不明白不要紧,看到下面就会明白了。
new get_index_spr
//定义SPR索引,为下面的循环作准备。这里定义成全局变量是因为下面生成菜单还需要它。
new g_nMenuPosition[33]
//定义菜单位置索引,否则程序不知道比如按下2是第一页还是第二页的2
        第三步,要显示实体,就先要进行实体缓存和赋值
语法格式
public plugin_precache()
{
   实体名称(自定义) = precache_model("目录/实体名称")
}
这里就需要一点技巧,我们可以写一个循环,让程序读取spr_list.ini里面的实体名称,然后逐个进行缓存赋值,这样就不需要大量的代码了。
先想象整个过程,程序读取第一行内容,然后转换成字符,同时以这个字符串赋值SPR文件,比如说
第一行读到的是"hello",然后就hello = prache_model("sprites/hello.spr"),然后计数器增加,接续循环,实体索引也随着增加,一直到满足条件跳出循环。
好了,我们需要实体索引,上面已经定义过了,但是我们必须要把它初始化,
get_index_spr = 0
接下来需要对文件操作,具体的参数格式可以在file.inc里面找到。
这里我们首先需要建立路径。
定义配置文件名变量和SPR文件名变量,并且小于64字符,
new sprite_file[64]
new sprite_config[64]
build_path(sprite_config, 63, "$basedir/config/spr_list.ini")
这样,我们就建立了需要操作的文件的路径,其中$basedir表示AMX基本路径,也就是addons/amx/
定义变量,分别用作行数和计数器
new len, pos = 0
好了,现在就具备了所有的条件,我们可以开始循环读取了。
if(file_exists(sprite_config))//先判断文件是否存在,
while(read_file(sprite_config,pos++,get_spr_name[get_index_spr],31,len))
FOR循环和WHILE循环的区别在于,FOR是先有条件后后循环,而WHILE是先循环直到满足条件。
上面的意思就是,读取配置文件,把第一行内容进行索引存入get_spr_name数组,
if( sprite_config[0] == ';' || !len ) continue
这里需要一个判断,如果行首出现“;”符号或者空行,则忽略跳过。
format(sprite_file,63,"sprites/%s.spr",get_spr_name[get_index_spr])
格式化字符串,得到SPR文件全称,%s表示替代调换字符串,意思就是程序把第一行读取到的字符串进行替代调换后得到SPR文件的路径和全称,并且赋值给变量sprite_file。然后就可以缓存SPR文件了。
get_spr_precached[get_index_spr] = precache_model(sprite_file)
get_index_spr++//索引值增加。
这里应该就看明白了吧

到此为止,我们就用循环和数组、配置文件完成了SPR列表中的SPR文件缓存赋值。
如果感兴趣的朋友有不太明白的地方请跟贴!只要我明白的一定为大家解答!
 楼主| 发表于 2005-9-8 15:35:42 | 显示全部楼层 来自 广东广州

回复: 【分享】浅谈表情插件制作经验

第四步就是菜单的制作了。
我们可以这么想,如果一页菜单数量大于SPR数量,那么就要进行分页显示,那么就需要为菜单的位置进行索引,否则程序就不能判断例如第二个选项是哪儿的,是第一页?还是第二页?那么,我们需要做的是哪些?当然是为菜单开始位置,结束位置,行数,热键定义变量
public ShowMenu( id, pos )//定义菜单ID,计数
{
        if( pos < 0 ) return如果没有计数器就停止

        new i, j = 0//定义变量为循环准备。
        new nKeys, nStart, nEnd, nLen//定义热键,开始位置、结束位置和行数变量。
        new faceMenuBody[512]//定义菜单总字符串。

        nStart = pos * MAX_DISPLAY//开始位置等于计数*最大显示,也就是8

        if( nStart >= MAX_SPRITES )//判断如果开始位置值已经大于最大SPR值,就停止。因为我们需要的是SPR的值。
                nStart = pos = g_nMenuPosition[id] = 0//初始化菜单索引,计数,开始位置。

        nLen = format( faceMenuBody, 511, "\yPleaseChoose^n^n",pos + 1)//开始显示菜单主题
        nEnd = nStart + MAX_DISPLAY//定义结束位置。
        nKeys = (1<<9)//定义最后行热键。

        if( nEnd > MAX_SPRITES ) nEnd = MAX_SPRITES//判断如果结束位置大于SPR值那么初始化结束位置为SPR值。这样就使得菜单数量刚好与SPR数量相等。

        for( i = nStart; i < nEnd; i++ )//开始循环。
        {
                nKeys |= (1<<j++)//循环定义热键
                nLen += format( faceMenuBody[nLen], (511-nLen), "\w%d. %s^n\w", j, get_spr_name )
//格式化上面已经缓存好的get_spr_name的SPR文件名,这里是按索引来读取的。
        }

        if( nEnd != MAX_SPRITES )//如果菜单结束值与SPR值不相等。
          {
                format( faceMenuBody[nLen], (511-nLen), "^n9. More...^n0. %s", pos ? "Back" : "Exit" )
//则显示更多选择或者返回。
                nKeys |= (1<<8)/定义热键。
        }
          else format( faceMenuBody[nLen], (511-nLen), "^n0. %s", pos ? "Back" : "Exit" )
       
        show_menu(id,nKeys,faceMenuBody,-1)//显示菜单
}
这样就利用循环和数组完成了菜单的多页显示,开始看不明白不要紧,慢慢来,我也是整整看了半天加上自己测试才明白过来的。那么,在显示SPR文件的时候,程序如何知道我们选择的是第一页的第二选择还是第二页的第二选择呢?
g_nMenuPosition[id]*MAX_DISPLAY + key
这样就可了,有了菜单的ID,页数,和页数相应的热键,就可以返回我们需要的值了。那么显示实体部分就可以这样。
new spr_name = g_nMenuPosition[id]*MAX_DISPLAY + key
write_short( get_spr_precached[spr_name] )
        至于菜单的命令模块如果实现多页菜单的命令,我们只需要定义菜单返回或者更多,菜单退出两个就命令就可以
case 8: ShowMenu( id, ++g_nMenuPosition[id] )//返回或者更多
case 9: ShowMenu( id, --g_nMenuPosition[id] )//退出。
其默认命令只需要用参数替代调换就可以了。

这样我们就完成了整个过程了。

时间匆忙,经验不足,其中难免有解释不清楚的地方和疏漏错误的地方,感兴趣的朋友可以跟贴发问,也请高手多多指点!!
回复

使用道具 举报

发表于 2005-9-8 16:53:36 | 显示全部楼层 来自 四川成都

回复: 【分享】浅谈表情插件制作经验

严格的说每页菜单的最大数目用不着宏定义的,感觉有点多余哦。。。


nKeys |= (1<<j++)//循环定义热键    不错的想法。。。。学习


不知道这是不是你原来说的显示多页菜单更好的方法??把其他的都分享了吧 :dribble:

ps:希望下次能把代码换个地方完整的放,现在这样看起来不连贯,很费眼啊。。。


顶。。。
回复

使用道具 举报

 楼主| 发表于 2005-9-8 18:57:17 | 显示全部楼层 来自 广东广州

回复: 【分享】浅谈表情插件制作经验

不会啊,因为你并不知道具体有多少菜单,但是,假如,如果多于一页,
那么,除了12345678八个正常的选择外,还需要9(更多,或者返回),0(退出)
所以,定义最大显示数量,但是这八个选择确是由变量生成的。


  1. #include <amxmod>
  2. #include <amxmisc>

  3. #define MAX_DISPLAY 8
  4. #define MAX_SPRITES 24
  5. #define TE_PLAYERATTACHMENT 124

  6. new get_spr_name[MAX_SPRITES][32]
  7. new get_spr_precached[MAX_SPRITES]
  8. new get_index_spr
  9. new g_nMenuPosition[33]

  10. public plugin_init()
  11. {       
  12.         register_plugin("Face Show", "0.2", "[Grief.QQ]")
  13.         register_menucmd( register_menuid("\yPleaseChoose"), 1023, "MenuCommand" )

  14.         register_clcmd( "say /face","DoShowMenu", 0 )

  15.         return PLUGIN_CONTINUE
  16. }

  17. public plugin_precache()
  18. {
  19.   get_index_spr = 0
  20.   new sprite_file[64]
  21.   new sprite_config[64]
  22.   build_path(sprite_config, 63, "$basedir/config/spr_list.ini")
  23.   new len, pos = 0

  24.   if(file_exists(sprite_config))
  25.   {
  26.     while(read_file(sprite_config,pos++,get_spr_name[get_index_spr],31,len))
  27.     {
  28.       if( sprite_config[0] == ';' || !len ) continue
  29.       format(sprite_file,63,"sprites/%s.spr",get_spr_name[get_index_spr])
  30.       if(file_exists(sprite_file))
  31.       {
  32.         get_spr_precached[get_index_spr] = precache_model(sprite_file)
  33.         get_index_spr++
  34.       }
  35.     }
  36.   }
  37.   return PLUGIN_CONTINUE
  38. }

  39. public MenuCommand( id, key )
  40. {
  41.         switch( key )
  42.         {
  43.                 case 8: ShowMenu( id, ++g_nMenuPosition[id] )
  44.                 case 9: ShowMenu( id, --g_nMenuPosition[id] )
  45.                                 default:{
  46.                                  
  47.                           if(!is_user_alive(id))
  48.                                         {
  49.                                         set_hudmessage(200, 100, 0, -1.0, 0.3, 0, 1.0, 5.0, 0.1, 0.2, 3)
  50.                                         show_hudmessage(id,"Sorry,You Are Dead!!!")
  51.                                         return PLUGIN_HANDLED
  52.                                         }
  53.                                
  54.                         new spr_name = g_nMenuPosition[id]*MAX_DISPLAY + key
  55.                                
  56.                         message_begin(MSG_ALL,SVC_TEMPENTITY,{0,0,0},id )
  57.                           write_byte ( TE_PLAYERATTACHMENT )
  58.                           write_byte(id)
  59.                           write_coord(60)
  60.                           write_short( get_spr_precached[spr_name] )
  61.                           write_short(30)
  62.                           message_end()
  63.                                          
  64.                           return PLUGIN_HANDLED
  65.                                                           }
  66.           }

  67.           return PLUGIN_HANDLED
  68. }

  69. public ShowMenu( id, pos )
  70. {
  71.         if( pos < 0 ) return

  72.         new i, j = 0
  73.         new nKeys, nStart, nEnd, nLen
  74.         new faceMenuBody[512]

  75.         nStart = pos * MAX_DISPLAY

  76.         if( nStart >= MAX_SPRITES )
  77.                 nStart = pos = g_nMenuPosition[id] = 0

  78.         nLen = format( faceMenuBody, 511, "\yPleaseChoose^n^n",pos + 1)
  79.         nEnd = nStart + MAX_DISPLAY
  80.         nKeys = (1<<9)

  81.         if( nEnd > MAX_SPRITES ) nEnd = MAX_SPRITES

  82.         for( i = nStart; i < nEnd; i++ )
  83.         {
  84.                 nKeys |= (1<<j++)
  85.                 nLen += format( faceMenuBody[nLen], (511-nLen), "\w%d. %s^n\w", j, get_spr_name[i] )
  86.         }

  87.         if( nEnd != MAX_SPRITES )
  88.           {
  89.                 format( faceMenuBody[nLen], (511-nLen), "^n9. More...^n0. %s", pos ? "Back" : "Exit" )
  90.                 nKeys |= (1<<8)
  91.         }
  92.           else format( faceMenuBody[nLen], (511-nLen), "^n0. %s", pos ? "Back" : "Exit" )
  93.        
  94.         show_menu(id,nKeys,faceMenuBody,-1)
  95. }

  96. public DoShowMenu( id, lvl, cid )
  97. {
  98.         if( cmd_access( id, lvl, cid, 1 ) )
  99.                 ShowMenu( id, g_nMenuPosition[id] = 0 )
  100.         return PLUGIN_HANDLED
  101. }
复制代码
回复

使用道具 举报

发表于 2005-9-8 22:12:17 | 显示全部楼层 来自 四川成都

回复: 【分享】浅谈表情插件制作经验

Post by '[Grief.QQ
']不会啊,因为你并不知道具体有多少菜单,但是,假如,如果多于一页,
那么,除了12345678八个正常的选择外,还需要9(更多,或者返回),0(退出)
所以,定义最大显示数量,但是这八个选择确是由变量生成的。



不足8项的,或多于8的,你的程序里面不都考虑了吗??

我说的多余的意思是,你用了宏定义,可事实上既没有节省代码,也不是为了修改方便(没有必要去改,是不是呢?你程序的其他部分都考虑了的),这样看来直接用8不是更合适么?
回复

使用道具 举报

 楼主| 发表于 2005-9-9 00:34:23 | 显示全部楼层 来自 广东广州

回复: 【分享】浅谈表情插件制作经验

啊,你是说这个意思,我是为了代码能看的明白才这么定义的,不然,无端端冒出个8来,
不太明了。呵呵,一样,一样地。
回复

使用道具 举报

游客
回复
您需要登录后才可以回帖 登录 | 注个册吧

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