XEricSin 发表于 2019-10-19 01:56:24

在ERA上做MOD的教程

这个帖子的目的是给大家介绍一下制作mod的一些基本知识和工具。

更完整的英文教程可以参阅这里:http://heroescommunity.com/viewthread.php3?TID=32519

原版的英三做MOD一般有2个途径,一个是修改原data目录下的txt文件,另一个是直接修改exe文件。
修改txt的方法很简单,大家可以找找相关教程,一看就会。
修改exe的方法一般人不建议尝试。
这样做对WOG版也是适用的,但是兼容性比较差。所以有了era作为一个平台,让不同作者的mod都能方便的加载卸载。

era系统的核心是游戏根目录下的mods文件夹。每个mod都要在mods文件夹下创建一个单独的文件夹,所有的更改全部都存在这个文件夹里面。最后编辑List.txt内容来设置要启用的MOD,顺序很重要。和mod管理器功能一样但顺序相反。
比如,D:\Games\HoMM3 ERA 2.8.8\Mods 下有WOG,ERA中文界面等等mod。

我自己的mod比如逐鹿MOD,就放在逐鹿城文件夹中,新增的东西和要替换的东西都放在自己的mod文件夹里面,不要更改别的MOD。如果你要替换原来的某个文件如:根目录/data/ZCrTrait.txt,那么就要按照同样的文件夹结构在你的mod下放入新的文件,比如:你的mod/data/ZCrTrait.txt。一般来说原版的资源文件都是放在lod文件里面的,mod的资源文件则一般放在pac里面。pac文件的名字不重要,但里面的资源文件的名字一定要一样才能替换。


然后我们看看常用工具,这些工具都可以在根目录的tools文件夹里找到,也可以在wog菜单中找到。我的版本不一样,大家认图标哈。


1. MMAchive查看/编辑LOD、PAC、SOUND这类的资源包
基本上所有的资源都在里面,有时候为了方便,也把txt文件留在data文件夹中。

以我的逐鹿城为例,我在mod的data文件夹下创建pac文件,装我的资源。wav声音文件放到snd里面,其余的放到pac里面。这些资源包的名字不限个数不限。



2. TxtEdit表格编辑器,比如编辑ZCrTrait.txt生物属性。这个太简单了。
excel 也是可以用的,但这个更好用。

3. DefPreview查看def和导出图片
点圈中的这个按钮可以导出图片和图片的结构hdl文件,用于DefTool编辑def。



4. DefTool编辑def,可以用DefPreview打包导出DefTool专用格式
用deftool创建或者打开hdl文件,修改动图的每一帧。例如生物def,def类别是42,有移动,站立等等动作。魔法动画的def类别是40。其他的可以自己去看。
编辑好了就按右边的扳手图标把图片打包成def。如果你手上有gif动图,可以把图片提取成bmp,然后用deftool转到def里,再放到游戏里。具体可以参考其他帖子。


5. ERM编辑器编辑erm和ert文件用的
这个是用来编辑脚本的。erm是脚本,ert是脚本附带的文本文件(可不用)。
除了自带的erm编辑器,基本上任何一个文本编辑器都可以,windows的记事本除外。
erm怎么写有哪些功能,请参阅其他帖子。

今天到此为止,希望更多的朋友能够加入mod中来,我们最需要的就是能够制作精美生物def的玩家了。

XEricSin 发表于 2019-10-26 14:19:28

本帖最后由 XEricSin 于 2019-10-27 19:36 编辑

今天讲,如何制作生物的战场动图.def文件。

H3里生物动图的格式是.def,类似大家都知道的gif动图,是由一张张静态图组成的。
所以制作def的原材料是一堆整套动作的图片。如果你手头有gif动图,需要先提取每一帧成bmp图片。



图片格式必须
是450*400像素的8位(256色)的bmp图。
单格生物的宽度一般在35-45px,双格生物乘以2。
生物高度在90px左右,弓箭手高度为80px,大恶魔高度为110px。
【注意】如果你要自己做3d模型,镜头角度应该为水平,加5度水平转动。




工具是前面提到的deftool,era自带。

1. 打开后长这样,一定要勾选默认背景色:


2. 在frames页面绑定动作和图片:
一般攻击动作需要5到10张图,
站立、转向、开始移动1张图就够了,
如果生物不能射击和施法,动作图片可以不放



3. 在shadow页面设置生物影子:


4. 在reposition页面微调图片位置:


5. 最后在主页面生成def:



6. 生成的def文件用defpreview工具打开,检查是否有问题。
没问题就用MMA工具导入lod或pac包里面。
有问题就修改或重做。


结束。


XEricSin 发表于 2020-1-9 00:41:07

本帖最后由 XEricSin 于 2020-1-9 00:51 编辑

今天我们来讲ERM。

可以参考一看就懂的ERM新手教程

ERM是在ERA中专门用来实现各种修改的语言。语言简单,对基本修改很方便,但对复杂的修改需要配合内存相关的知识或者插件。

首先讲如果手上有脚本怎么用。
我之前说过,新增的东西和要替换的东西都放在自己的mod文件夹里面,不要更改别的MOD。
假设我把我的脚本都放到一个mod里,mod命名为逐鹿城。按照下图,创建相同的文件夹结构:游戏目录/Mods/逐鹿城/data/s

然后把自己的erm脚本文件都贴到s文件夹下就可以了。如果erm配了ert文件就一起贴进来,没有就不用管。

现在我们讲怎么写ERM脚本。
我这里会涉及到一些基本语法,和一些ERA2.88版的新语法。

必备工具:
1. ERM帮助,所有基本知识都在里面,可以在你下载的ERA开始菜单中找到。ERM的语法现在已经增加了不少内容,但任然没有更新帮助。
2. ERM编辑器,可用ERA开始菜单中自带的,也可以用任意一个代码编辑器。
我再提供一个EM编辑器。64位的是我使用的,已经配好了ERM文件的配置文件。
链接:https://pan.baidu.com/s/1VutKix3znvePQ4WNQUE4oQ
提取码:2eei

正文开始:
一般一个脚本会张这个样子:


ZVSE表示脚本开始,如果前面或者中间有其他字符("aZVSE"," ZVSE","ZV SE")是不合法的,程序会自动无视整个脚本。

命令
        脚本由一句句命令或语句组成。一句合法命令被!和;夹着。
      !表示命令开头,只能有下面4种合法写法,其他组合都会报错比如"!!!"。
                !#表示开图时执行一次,称为指令器。
                !?表示某事件前触发,称为触发器。
                !$表示某事件后触发,称为触发器。
                !!表示事件触发后要执行的代码开始,称为接收器。
                每一个触发器后可以跟随多个接收器,即多个执行的命令,直到下一个!。
      ;表示命令结束。
      没有夹在!和;中间的字符都是被程序无视的注释,给人看的。
      
      例子:指令器
      !#MA:A0/111; [游戏开始前执行一次,把0号兵(枪兵)的攻击力设为111]
      
      例子:触发器-接收器
      红色的部分夹在!和;中间是有效命令,其他的都是被程序无视的。

      !?PI;[游戏地图生成后触发]
      !!MA:A0/888;[枪兵攻击力设为888]
      这里是垃圾字符8sdhh88*&%
      !!MA:A1/777;[戟兵攻击力设为777],这句仍属于!?PI触发器。

      !?GM0;[游戏加载时触发]
      !!MA:A0/999;[枪兵攻击力设为999]
      *看ERM帮助了解更多触发器和接收器

从上面可以看到每个触发器和接收器后面都跟了两个大写字母,表示已经由开发者定义好的模块。
比如PI代表Post Instruction是载入WOG选项后的阶段。
比如MA代表Monster Attribute是怪物属性模块。MA:A表示MA中的Attack,即生物的攻击力。MA:A后面接参数,由/隔开,具体要接几个参数和什么数值,需要根据erm帮助中记录的规则来。
关于各种模块,请参阅erm帮助。

变量      
        变量是重要的数值储存单元。变量模块由VR表示。常用的变量有整数变量(v全局变量 y本地变量)和字符串变量(z全局变量)。
      数值变量有常规的运算:赋值S,加+,减-,乘*,除:,和求余%。注意,ERM中的运算是从左往右的,没有先乘除后加减。      
      字符串变量有赋值S和连接+两个运算。字符串用^^夹起来。
      例子:
      !?PI;
      !!VRv100:S1 +1 -1 *1 :1 %1;[第一百个v变量进行运算]
      !!IF:M^%V100^;

      !!VRz100:S^hi^ +^there^;
      !!VRz101:S^a^+z100;
      !!IF:L^%Z101^;[不弹框打印]

函数
        FU是ERM的函数模块。函数模块允许玩家自定义函数,是复杂MOD必备的东西。每个FU函数自带了16个整数变量x变量。
      例子:自定义编号1234567的函数,即当这个函数触发时执行哪些命令。
      !?FU1234567;
      !!IF:M^FU1234567 x1=%X1 x2=%X2^;[执行一个弹窗命令,显示前两个参数值]
      
      这里是ERA2.88版后支持新语法,可以不使用函数编号,而是给函数命名。其实是编译时程序自动分配一个函数编号。
      !?FU(func_1);
      !!IF:M^FU(func_1) x1=%X1 x2=%X2^;
      
      定义好了函数,就要在合适的敌方调用。定义时FU编号后就直接;结束,在调用时,一定要加:P表示参数。如果不使用参数P后可以空。
      比如,可以在!#指令器下调用
      !#FU1234567:P; [没有传入参数,使用默认参数]
      
      比如,可以在!?触发器下调用
      !?FU7654321;
      !!FU1234567:P1/1; [调用刚才定义好的FU1234567,并传入x1和x2两个参数]
      !!FU(func_1):P2/2; [调用刚才定义好的FU(func_1),并传入x1和x2两个参数]

        函数还可以将参数设为返回值。
      !!FU1234567:P0/?y1; [调用刚才定义好的FU1234567,并传入x1=0和把x2的值赋给y1,由于x2在函数中没有赋值操作,y1将会得到x2的默认值]
       

条件
        很多时候我们想在某些特定情况或者条件下执行某些命令,就需要条件功能。
      ERM的条件有与&和或|。与或一般不混用,因为erm的处理是从左往右的,没有优先级的。
      但多个与和多个或是可以分别连用的。如A与B与C(&A/B/C),A或B或C(|A/B/C)。
      条件是可以直接接着触发器和接收器后面的,比如!?FU123&v1=1;意思是当v1=1时,执行FU123。
      但这里我们主要讲if...el...en的格式。每一个if必须对应一个en,el表示否则,不必要。注意他们的结尾是冒号加分号:;

      例子:
      !?FU1234567;
      !!VRv100:C1/2; [把v100和v101分别设为1和2,参阅erm帮助]

      !!if&v100=1/v101=2:; [当v100=1且v101=2时]
      !!IF:M^v100=1且v101=2^;
      !!en:;

      !!if|v100=1/v101=1:; [当v100=1或v101=1时]
      !!IF:M^v100=1或v101=1^;
      !!en:;

      注意代码是按顺序执行的。
      下面这个会分别检测1,2,3,一共三次。

      !?FU1234567;
      !!VRv100:S2;

      !!if&v100=1:;[如果等于1]
      ...
      !!en:;

      !!if&v100=2:;[如果等于2]
      ...
      !!en:;

      !!if&v100=3:;[如果等于3]
      ...
      !!en:;

      下面这个会按顺序检测,直到第一个真确后结束整个函数。

      !?FU1234567;
      !!VRv100:S2;

      !!if&v100=1:;[如果等于1]v100=2时这个会检测但里面的命令不会执行
      ...
      !!FU:E;
      !!en:;

      !!if&v100=2:;[如果等于2]v100=2时这个会检测且里面的命令会执行
      ...
      !!FU:E; [表示退出当前函数]
      !!en:;

      !!if&v100=3:;[如果等于3]v100=2时这个不会检测,因为前面就退出了
      ...
      !!FU:E;
      !!en:;

         下面这个用了if...el...en的结构。效果会跟上面这个基本一样。
      注意,el&和el|的用法是ERA2.88的新语法。ERA2.46用不了。

      !?FU1234567;
      !!VRv100:S2;
      !!if&v100=1:;[如果等于1]
      ...
      !!el&v100=2:;[如果等于2]
      ...
      !!el&v100=3:;[如果等于3]
      ...
      !!el:;[其他]
      ...
      !!en:;

循环      
        函数循环不多说了,遍历英雄,遍历城镇等等时必用的功能。
      DO循环,相当于for循环。
      !?FU1234567;
      !!DO12345678/0/6/1:P;
      
      !?FU12345678;
      !!IF:M^x16=%X16^;
      
      Go to lable循环,相当于while循环。
      !?FU1234567;
      !!VRy16:S0;
      !!VRy17:S6;
      [:LABEL] 这句是特殊的合法命令,虽然没有!和;
      !!IF:M^y16=%Y16^;
      !!SN&y16<=y17:G; [表示跳到LABLE这句继续向下执行,用一个&y16<=y17来判断是否结束循环]
      
今天讲了很多ERM的核心内容。望大家自己练习。如果要写出高级的脚本功能,ERM帮助的相关模块一定要熟悉。

有兴趣的可以自己写一些下面两个作业。
作业1.在游戏开始前修改枪兵的防御力为55,判断55的奇偶,并打印。
作业2.定义一个函数,在游戏开始时执行,变量v100设为0,加20次1,每加一次打印新的v100的值。

XEricSin 发表于 2020-5-23 05:27:10

今天我们来讲一讲怎么从网上获取新的特效动画DEF。

网上有很多gif动图,但是这些动图绝大多数是半透明图片混合到底色上生成的假半透明图片。几乎没有H3可以直接使用的透明像素和不透明像素分开的图片。

幸好,这些动图中很大部分是黑色或深灰色的底色。这使得我们可以通过一些手段把这些假半透明变成抖动式的半透明。
我利用python写了几个函数。使用时,调用 extractGIFframes2() 和 generateBMPforDEF() 两个函数,生成bmp图片,然后可以用DEF编辑器打包城DEF。
#例子,两个函数输入的文件夹地址必须一致
extractGIFframes2('C:/folder')
generateBMPforDEF('C:/folder')


转化gif前,先创建一个文件夹比如叫effect1,把下载的gif动图放到文件夹中。


生成可用图片后变成这个样子:


放入DEF编辑器中的效果:




以下为python代码,需要按照相关库:
import numpy as np
import random
import os

import cv2
from matplotlib import pyplot as plt

#in_path = bmp/gif图片
#out_path = bmp/gif图片
#针对黑色背景的GIF
#根据HSV的V值随机剔除像素
#比方法2更耐受背景噪音
def removeTransparency3(in_path, out_path):
    image = cv2.imread(in_path, cv2.IMREAD_COLOR)
    image1 = image.copy()
    image2 = cv2.blur(image, (3, 3)) # 使用均值后的图像决定像素是否清零

    HSV = cv2.cvtColor(image2,cv2.COLOR_BGR2HSV)
    H,S,V = cv2.split(HSV)

    V_Threshold_Lower = (V.max())*1.05 # 前五个像素点选最大值作为阈值
    V_Threshold_Upper = 255 - V_Threshold_Lower
   
    #print(V_Threshold_Lower, V_Threshold_Upper)
    #print(V)
   
    for i in range(V.shape):
      for j in range(V.shape):
            if V < V_Threshold_Lower:
                image1=
            elif V < V_Threshold_Upper:
                r = random.random() # 0-1
                if r > (V - V_Threshold_Lower)/(V_Threshold_Upper - V_Threshold_Lower):
                  image1=

    # 对比原始图片和随机去除透明后的图片
    #plt.figure(figsize=(60,20))
    #plt.subplot(121); plt.imshow(image)
    #plt.subplot(122); plt.imshow(image1)
    #plt.show()

    cv2.imwrite(out_path, image1)
   

def generateBMPforDEF(framesFolder):
    #输入目标文件夹的路径
    filedir0 = framesFolder
    #设置输出文件夹在输入文件夹内
    filedir1 = filedir0 + '/generated pics'
    #创建新文件夹
    if not os.path.exists(filedir1):
      os.makedirs(filedir1)
    #获取目标文件夹中的文件名称列表
    filenames = os.listdir(filedir0)
    #遍历文件名
    for filename in filenames:
      extension = filename.split('.')[-1]
      if extension == 'bmp'or extension == 'png':
            filepath0 = filedir0 + '/' + filename
            filepath1 = filedir1 + '/' + filename[:-3] + 'bmp'
            #print(filepath0)
            print(filepath1)
            removeTransparency3(filepath0, filepath1)

def extractGIFframes2(GIFfolder):

    '''
    约定文件夹中只有一个xxx.gif
    xxx的帧文件保存为GIFfolder/xxx-index.bmp
    路径中用一律使用'/'
    '''

    #输入目标文件夹的路径
    filedir0 = GIFfolder
    #设置输出文件夹在输入文件夹内
    filedir1 = filedir0 # 直接保存到当前目录

    #获取目标文件夹中的文件名称列表
    filenames = os.listdir(filedir0)
    #遍历文件名,应该保证只有一个gif文件
    for filename in filenames:

      if filename.split('.')[-1] == 'gif':
            filepath0 = filedir0 + '/' + filename

            x=cv2.VideoCapture(filepath0)
            i=1
            while True:
                filepath1 = '%s/%s-%02d.png' % (filedir1, filename.split('.'), i)
                ret, frame = x.read()
                if ret == True:
                  cv2.imwrite(filepath1, frame)
                  i+=1
                else:
                  return




cec0612734 发表于 2019-10-19 10:22:51

技术贴,顶起来。有兴趣的朋友就可以先自食其力,然后做大做强,造福更多玩家{:2_145:}

Vince 发表于 2019-10-19 10:46:04

先马再说!向版主致敬!

canary 发表于 2019-10-19 12:19:09

有时间弄个技术整合贴,把论坛所有相关教程贴整合下,方便有兴趣的坛友查询。

evildoor01 发表于 2019-10-19 13:57:20

这么短,真看完了,等后续课程,,,感觉这教程还只是个序章。

wogJJ 发表于 2019-10-19 14:13:54

谢谢楼主,有时间再学习学习

zsqzsqzsq 发表于 2019-10-19 20:23:47

很给力,顶楼主,还有后续的吗

你是我的神 发表于 2019-11-4 19:41:48

受教了,谢谢分享。

和风之夏 发表于 2019-11-6 13:54:19

观摩学习一下。太深奥了

chenjiren 发表于 2020-1-4 19:07:32


技术贴,顶起来。

XEricSin 发表于 2020-1-9 00:58:41

麻烦@贤知有您 附个最新版erm帮助链接过来。

琭琭有彧 发表于 2020-1-9 17:22:38

大佬出手就是不一样,顶一顶;tl;。

水仙湖 发表于 2020-1-10 23:24:32

感谢,学习!!!

深海的蛋黄骑士 发表于 2020-3-17 11:26:45

感觉就是类似flash里面的动画 逐帧动画 every frame shall be made 还是费时间啊

guxuan810511 发表于 2020-3-18 09:39:31

学习学习{:1_482:}

超级皮蛋 发表于 2020-3-18 11:21:34

感觉就是类似flash里面的动画 逐帧动画 every frame shall be made 还是费时间啊

83376526@qq.com 发表于 2020-4-10 10:33:11

大神厉害

db267 发表于 2020-4-17 01:08:56

新人先观摩下!自己研究研究看看!
页: [1] 2 3
查看完整版本: 在ERA上做MOD的教程

捐赠