1. 程式人生 > >遊戲開發——屬性計算器輔助工具——知道大家都懶

遊戲開發——屬性計算器輔助工具——知道大家都懶

序言

知道大家都懶,都挺忙的,於是提出給大家提供一個屬性計算器功能,

在遊戲中每一個場景物件都有著自己的屬性,例如移動速度,攻擊力,防禦力,幸運值,三維等一系列屬性值

屬性牽涉的最多的幾個問題,屬性的常規值,也就是裸屬性,裝備的屬性值,buff屬性值,技能屬性值等;

那也就牽涉了常規運算,賦值,相加,相乘等情況;

但是有的時候我們可能牽涉幾十個屬性,

 1 /**
 2      * 護盾效果最大值
 3      */
 4     @FieldAnn(fieldNumber = 800, describe = "護盾效果最大值")
 5     private transient long shield_max = 0;
6 /** 7 * 護盾效果每一次吸收固定傷害值 8 */ 9 @FieldAnn(fieldNumber = 801, describe = "護盾效果每一次吸收固定傷害值") 10 private transient long shield = 0; 11 /** 12 * 護盾效果每一次吸收傷害數值的百分比,配置是萬分比 13 */ 14 @FieldAnn(fieldNumber = 802, describe = "護盾效果每一次吸收傷害數值的百分比,配置是萬分比") 15 private transient
long shield_pro = 0; 16 /** 17 * 血符的最大值 18 */ 19 @FieldAnn(fieldNumber = 900, describe = "血符的最大值") 20 private transient long reply_max = 0; 21 /** 22 * 固定數值恢復 23 */ 24 @FieldAnn(fieldNumber = 901, describe = "固定數值恢復") 25 private transient long reply = 0; 26 /** 27
* 按照比例恢復 28 */ 29 @FieldAnn(fieldNumber = 902, describe = "按照比例恢復") 30 private transient long reply_pro = 0; 31 /** 32 * 間隔時間觸發直接恢復滿 33 */ 34 @FieldAnn(fieldNumber = 903, describe = "間隔時間觸發直接恢復滿") 35 private transient long reply_all = 0; 36 37 /** 38 * 物理攻擊_最小 39 */ 40 @FieldAnn(fieldNumber = 401, describe = "物理攻擊_最小") 41 @Column(name = "q_attack_min", nullable = false, columnDefinition = "INT default 0") 42 protected int attackMin; 43 /** 44 * 物理攻擊_最大 45 */ 46 @FieldAnn(fieldNumber = 401, describe = "物理攻擊_最大") 47 @Column(name = "q_attack_max", nullable = false, columnDefinition = "INT default 0") 48 protected int attackMax; 49 /** 50 * 魔法攻擊_最小 51 */ 52 @Column(name = "q_magic_attack_min", nullable = false, columnDefinition = "INT default 0") 53 @FieldAnn(fieldNumber = 402, describe = "魔法攻擊_最小") 54 protected int magicAttackMin; 55 /** 56 * 魔法攻擊_最大 57 */ 58 @Column(name = "q_magic_attack_max", nullable = false, columnDefinition = "INT default 0") 59 @FieldAnn(fieldNumber = 402, describe = "魔法攻擊_最大") 60 protected int magicAttackMax; 61 /** 62 * 物理防禦 63 */ 64 @Column(name = "q_defense", nullable = false, columnDefinition = "INT default 0") 65 @FieldAnn(fieldNumber = 403, describe = "物理防禦") 66 protected int defence; 67 /** 68 * 魔法防禦 69 */ 70 @Column(name = "q_magic_defence", nullable = false, columnDefinition = "INT default 0") 71 @FieldAnn(fieldNumber = 404, describe = "魔法防禦") 72 protected int magicDefence; 73 74 /** 75 * 最大血量 76 */ 77 @Column(name = "q_max_hp", nullable = false, columnDefinition = "LONG default 0") 78 @FieldAnn(fieldNumber = 400, describe = "最大血量") 79 protected long maxHp; 80 81 /** 82 * 命中率 83 */ 84 @Column(name = "q_hit", nullable = false, columnDefinition = "INT default 0") 85 @FieldAnn(fieldNumber = 405, describe = "命中率") 86 protected int hit;

大約就像這樣的情況,當然這只是其中一部分屬性;

以前我們做屬性的相加,相乘,只能按照一個屬性一個屬性去一一對應;

屬性配置方式

在屬性的配置方式中使用的形式,有例如mysql和hibernate的關係一對一的去解析屬性,但是這個只適合場景物件的基本裸屬性;

如果是裝備的屬性值或者技能,或者buff屬性值,如果還是這麼配置,蛋疼的事情就會出現;

如果在配置裝備屬性的時候還像裸屬性一樣一一對應,那麼可能會分瘋掉。

那麼可能大家都會想到json字串格式配置方式去實現屬性資料的配置,解析出來以後就可以直接賦值,或者運算;這確實是一種方式,沒問題;

但是在實際研發或上線運營過程中我們會發現字串配置錯誤的行為,比如大小寫,或者少字母等情況;

後來又改為數值型別的一一對應,比如200表示攻擊力,201表示防禦力這種形式;

這種配置方式減少了配置人員在配置屬性時候出錯的概率;

讀取屬性方式

屬性的讀取方式,如果是mysql和hibernate這種對應關係直接讀取就好;

    @Column(name = "q_attack_min", nullable = false, columnDefinition = "INT default 0")
    protected int attackMin;

在屬性上加入註解,就可以;

但是如果是buff那種,

403_10000,404_10000

如果是這種形式的屬性,我是如果讀取的呢?

首先我們建立一個新的註解類

 1 /**
 2  * 屬性註解,標註型別匹配
 3  * <br>
 4  * author 失足程式設計師<br>
 5  * blog http://www.cnblogs.com/shizuchengxuyuan/<br>
 6  * mail [email protected]<br>
 7  * phone 13882122019<br>
 8  */
 9 @Target(ElementType.FIELD)
10 @Retention(RetentionPolicy.RUNTIME)
11 public @interface FieldAnn {
12 
13     /**
14      * 數值型別
15      *
16      * @return
17      */
18     public int fieldNumber() default 0;
19 
20     /**
21      * 屬性描述欄位
22      *
23      * @return
24      */
25     public String describe() default "";
26 
27     /**
28      * 忽律欄位
29      *
30      * @return
31      */
32     public boolean alligator() default false;
33 }

這樣我們就可以在欄位上加入註解

1     /**
2      * 物理攻擊_最大
3      */
4     @FieldAnn(fieldNumber = 401, describe = "物理攻擊_最大")
5     @Column(name = "q_attack_max", nullable = false, columnDefinition = "INT default 0")
6     protected int attackMax;

然我們需要將類的欄位都反射解析出來靜態儲存

 1 /**
 2  * 型別的屬性欄位結構體
 3  * <br>
 4  * author 失足程式設計師<br>
 5  * blog http://www.cnblogs.com/shizuchengxuyuan/<br>
 6  * mail [email protected]<br>
 7  * phone 13882122019<br>
 8  */
 9 public class FieldStruc {
10 
11     /**
12      *
13      */
14     private final HashMap<String, Field> mapByName = new HashMap<>();
15     /**
16      *
17      */
18     private final HashMap<Integer, HashSet<Field>> mapfByNumber = new HashMap<>();
19 
20     public FieldStruc() {
21     }
22 
23     public FieldStruc(Class<?> clazz) {
24         actionClass(clazz);
25     }
26 
27     /**
28      * 如果 註解{@link FieldAnn alligator()} true 表示欄位不參與計算忽律快取
29      *
30      * @param clazz
31      */
32     protected void actionClass(Class<?> clazz) {
33         Field[] declaredFields = clazz.getDeclaredFields();
34         for (Field field : declaredFields) {
35             if (Modifier.isStatic(field.getModifiers())
36                     || Modifier.isFinal(field.getModifiers())) {
37                 continue;
38             }
39 
40             field.setAccessible(true);
41 
42             FieldAnn annotation = field.getAnnotation(FieldAnn.class);
43             if (annotation != null) {
44                 if (annotation.alligator()) {
45                     /*忽律欄位*/
46                     continue;
47                 }
48                 if (annotation.fieldNumber() > 0) {
49                     if (!mapfByNumber.containsKey(annotation.fieldNumber())) {
50                         mapfByNumber.put(annotation.fieldNumber(), new HashSet<>());
51                     }
52                     mapfByNumber.get(annotation.fieldNumber()).add(field);
53                 }
54             }
55 
56             mapByName.put(field.getName(), field);
57         }
58         Class<?> superclass = clazz.getSuperclass();
59         if (superclass != null) {
60             actionClass(superclass);
61         }
62     }
63 
64     /**
65      * 欄位名稱
66      *
67      * @return
68      */
69     public HashMap<String, Field> getMapByName() {
70         return mapByName;
71     }
72 
73     /**
74      * 欄位數值型別 key 值來源 {@link FieldAnn}
75      *
76      * @return
77      */
78     public HashMap<Integer, HashSet<Field>> getMapfByNumber() {
79         return mapfByNumber;
80     }
81 }

如何使用

基礎弄完了,我們需要知道如何使用

 1 /**
 2  *
 3  * <br>
 4  * author 失足程式設計師<br>
 5  * blog http://www.cnblogs.com/shizuchengxuyuan/<br>
 6  * mail [email protected]<br>
 7  * phone 13882122019<br>
 8  */
 9 public class TestField {
10 
11     private static final FieldStruc FIELD_STRUC = new FieldStruc(TestField.class);
12 
13     @FieldAnn(fieldNumber = 1)
14     private int i;
15     @FieldAnn(fieldNumber = 2)
16     private double d;
17     @FieldAnn(fieldNumber = 3)
18     private float f;
19     @FieldAnn(fieldNumber = 4)
20     private long l;
21     @FieldAnn(fieldNumber = 5)
22     private short s;
23 
24     public int getI() {
25         return i;
26     }
27 
28     public void setI(int i) {
29         this.i = i;
30     }
31 
32     public double getD() {
33         return d;
34     }
35 
36     public void setD(double d) {
37         this.d = d;
38     }
39 
40     public float getF() {
41         return f;
42     }
43 
44     public void setF(float f) {
45         this.f = f;
46     }
47 
48     public long getL() {
49         return l;
50     }
51 
52     public void setL(long l) {
53         this.l = l;
54     }
55 
56     public short getS() {
57         return s;
58     }
59 
60     public void setS(short s) {
61         this.s = s;
62     }
63 
64     /**
65      * 追加屬性
66      *
67      * @param fieldStr id_值,id_值,id_值,id_值,id_值
68      */
69     public void setValue(String fieldStr) {
70         /*按照關鍵字切割*/
71         String[] split = fieldStr.split(",|,");
72         if (split != null && split.length > 0) {
73             for (String string : split) {
74                 String[] split1 = string.split("_");
75                 if (split1 != null && split1.length == 2) {
76                     FIELD_STRUC.setFieldValue(Integer.valueOf(split1[0]), this, split1[1]);
77                 }
78             }
79         }
80     }
81 
82    
83     @Override
84     public String toString() {
85         return "TestField{" + "i=" + i + ", d=" + d + ", f=" + f + ", l=" + l + ", s=" + s + '}';
86     }
87 }

建立測試程式碼,測試一下如何使用賦值

    public static void main(String[] args) {
       t0();
    }

    public static void t0() {
        TestField tf1 = new TestField();
        tf1.setValue("1_2000,2_2000");
        System.out.println(tf1);
        tf1.addValue("3_2000,4_2000");
        System.out.println(tf1);
        tf1.addValue("1_10000,5_2000");
        System.out.println(tf1);
    }

輸出結果:

1 --- exec-maven-plugin:1.2.1:exec (default-cli) @ com.dyf.tools.utils ---
2 TestField{i=2000, d=2000.0, f=0.0, l=0, s=0}
3 TestField{i=2000, d=2000.0, f=2000.0, l=2000, s=0}
4 TestField{i=12000, d=2000.0, f=2000.0, l=2000, s=2000}
5 ------------------------------------------------------------------------
6 BUILD SUCCESS
7 ------------------------------------------------------------------------
8 Total time: 0.766 s
9 Finished at: 2018-04-02T10:36:13+08:00

複雜運算方式

通常我們屬性在參與計算的時候,比如,buff按照固定數值提高攻擊力,按照比例提升攻擊力等情況;

通常情況,這樣的話,我給數值策劃停供兩個欄位,一個欄位配置的是屬性的固定數值,

比如401-200,表示攻擊力提升200點固定數值

還有一個萬分比提升比例方式同樣是:401-2000;表示攻擊力需要提升20%;

這樣牽涉的問題就是,在運算的時候,需要根據配置去找到對於的屬性,做正常的操作;

在賦值運算之前我們需要先處理型別轉換問題,

在此就不bb直接上輔助類吧:

  1 package com.tools;
  2 
  3 import java.math.BigDecimal;
  4 import java.math.BigInteger;
  5 
  6 /**
  7  * 輔助型別轉換,泛型型別轉換
  8  * <br>
  9  * author 失足程式設計師<br>
 10  * blog http://www.cnblogs.com/shizuchengxuyuan/<br>
 11  * mail [email protected]<br>
 12  * phone 13882122019<br>
 13  */
 14 public class ConvertTypeUtil {
 15 
 16     /**
 17      * 常量型別
 18      */
 19     public enum TypeCode {
 20         /**
 21          * 預設值,null
 22          */
 23         Default(ConvertTypeUtil.class),
 24         Boolean(java.lang.Boolean.class),
 25         Char(char.class),
 26         Date(java.util.Date.class),
 27         String(java.lang.String.class),
 28         Object(java.lang.Object.class),
 29         Byte(java.lang.Byte.class, byte.class),
 30         Short(java.lang.Short.class, short.class),
 31         Integer(java.lang.Integer.class, int.class),
 32         Long(java.lang.Long.class, long.class),
 33         Float(java.lang.Float.class, float.class),
 34         Double(java.lang.Double.class, double.class),
 35         BigInteger(java.math.BigInteger.class),
 36         BigDecimal(java.math.BigDecimal.class),;
 37 
 38         private Class<?>[] clazzs;
 39 
 40         private TypeCode(Class<?>... clazzs) {
 41             this.clazzs = clazzs;
 42         }
 43 
 44         public Class<?>[] getClazzs() {
 45             return clazzs;
 46         }
 47 
 48         /**
 49          *
 50          * @param clazz
 51          * @return
 52          */
 53         public static TypeCode getTypeCode(Class<?> clazz) {
 54             if (clazz != null) {
 55                 TypeCode[] values = TypeCode.values();
 56                 for (TypeCode value : values) {
 57                     for (Class<?> tmpClass : value.getClazzs()) {
 58                         if (tmpClass.equals(clazz)) {
 59                             return value;
 60                         }
 61                     }
 62                 }
 63             }
 64             return TypeCode.Default;
 65         }
 66 
 67         /**
 68          *
 69          * @param clazz
 70          * @return
 71          */
 72         public static TypeCode getTypeCode(String clazz) {
 73             if (clazz != null) {
 74                 TypeCode[] values = TypeCode.values();
 75                 for (TypeCode value : values) {
 76                     for (Class<?> tmpClass : value.getClazzs()) {
 77                         if (tmpClass.getName().equalsIgnoreCase(clazz) || tmpClass.getSimpleName().equalsIgnoreCase(clazz)) {
 78                             return value;
 79                         }
 80                     }
 81                 }
 82             }
 83             return TypeCode.Default;
 84         }
 85 
 86     }
 87 
 88     /**
 89      * 型別轉換
 90      *
 91      * @param obj
 92      * @param clazz
 93      * @return
 94      */
 95     public static Object changeType(Object obj, Class<?> clazz) {
 96 
 97         if (obj == null || clazz.isInstance(obj) || clazz.isAssignableFrom(obj.getClass())) {
 98             return obj;
 99         }
100 
101         TypeCode typeCode = TypeCode.getTypeCode(clazz);
102 
103         return changeType(obj, typeCode, clazz);
104     }
105 
106     /**
107      * 型別轉換
108      *
109      * @param obj
110      * @param typeCode
111      * @param clazz
112      * @return
113      */
114     public static Object changeType(Object obj, TypeCode typeCode, Class<?> clazz) {
115         /*如果等於,或者所與繼承關係*/
116         if (obj == null || clazz.isInstance(obj) || clazz.isAssignableFrom(obj.getClass())) {
117             return obj;
118         }
119 
120         switch (typeCode) {
121             case Char:
122                 throw new UnsupportedOperationException();
123             case String:
124                 return String.valueOf(obj);
125             case Boolean:
126                 return Boolean.valueOf((String) changeType(obj, TypeCode.String, String.class));
127             case Byte:
128                 return Byte.valueOf((String) changeType(obj, TypeCode.String, String.class));
129             case Short:
130                 return Short.valueOf((String) changeType(obj, TypeCode.String, String.class));
131             case Integer:
132                 return Integer.valueOf((String) changeType(obj, TypeCode.String, String.class));
133             case BigInteger:
134                 return BigInteger.valueOf((Long) changeType(obj, TypeCode.Long, Long.class));
135             case Long:
136                 return Long.valueOf((String) changeType(obj, TypeCode.String, String.class));
137             case Float:
138                 return Float.valueOf((String) changeType(obj, TypeCode.String, String.class));
139             case Double:
140                 return Double.valueOf((String) changeType(obj, TypeCode.String, String.class));
141             case BigDecimal:
142                 return BigDecimal.valueOf((Long) changeType(obj, TypeCode.Long, Long.class));
143             default: {
144                 return obj;
145             }
146         }
147     }
148 
149     /**
150      * 把物件轉化成 Byte
151      *
152      * @param obj
153      * @return
154      */
155     public static Byte toByte(Object obj) {
156         return (Byte) changeType(obj, TypeCode.Byte, Byte.class);
157     }
158 
159     public static byte tobyte(Object obj) {
160         if (obj == null) {
161             return 0;
162         }
163         return (Byte) changeType(obj, TypeCode.Byte, Byte.class);
164     }
165 
166     /**
167      * 把物件轉化成 Short
168      *
169      * @param obj
170      * @return
171      */
172     public static Short toShort(Object obj) {
173         return (Short) changeType(obj, TypeCode.Short, Short.class);
174     }
175 
176     public static short toshort(Object obj) {
177         if (obj == null