遊戲開發——屬性計算器輔助工具——知道大家都懶
序言
知道大家都懶,都挺忙的,於是提出給大家提供一個屬性計算器功能,
在遊戲中每一個場景物件都有著自己的屬性,例如移動速度,攻擊力,防禦力,幸運值,三維等一系列屬性值
屬性牽涉的最多的幾個問題,屬性的常規值,也就是裸屬性,裝備的屬性值,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 transientlong 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