日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java解析魔兽争霸3录像_Java解析魔兽争霸3录像W3G文件(五):Action和APM计算

發布時間:2024/8/1 java 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java解析魔兽争霸3录像_Java解析魔兽争霸3录像W3G文件(五):Action和APM计算 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在游戲進行中,玩家會進行各種操作,例如編隊、移動、技能、造建筑等,這些操作就是Action。APM(Actions Per Minute),表示每分鐘的操作次數,APM可以很好的反映玩家的手速和實力,當然也有高APM的菜鳥和低APM的高手。

在魔獸錄像文件中,需要記錄下玩家的操作,這些操作是記錄在游戲時間段(TimeSlot)數據塊中的,這在上一篇博文中有提到。

結構:

在TimeSlot中從第6字節開始到數據塊結尾的部分,包含多個玩家數據塊(CommandData Block):

1字節:玩家ID;

2~3字節:數據塊剩余字節數n;

4~n+4字節:包含該玩家對應的多個操作數據塊(ActionBlock)。

ActionBlock結構:

1字節:ActionID,表示操作類型,例如暫停游戲操作的ActionID是0x01;

剩余字節:Action參數,該部分結構需要根據ActionID來確定,有些Action沒有這部分。

由于Action類型很多,每種ActionID對應的ActionBlock結構這里不一一列出,下面列出一小部分:

1.暫停游戲

ActionID:0x01

字節數:1

計算APM:否

2.繼續游戲

ActionID:0x02

字節數:1

計算APM:否

3.編隊

ActionID:0x17

字節數:4+n*8

計算APM:是

結構:

1字節:ActionID;

2字節:隊伍編號(0~9);

3~4字節:選擇單位的數量n;

5~4+n*8:選擇單位

4.選擇編隊

ActionID:0x18

字節數:3

計算APM:是

結構:

1字節:ActionID;

2字節:隊伍編號(0~9);

3字節:未知0x03

APM計算:

由于暴雪官方并沒有提供APM的計算方式,所以APM計算的方式都是前輩牛人們總結出來的,不同的錄像分析軟件算出來的APM可能會有一些誤差。

APM的值等于玩家的有效Action數量除以玩家游戲時間的分鐘數。

一個ActionBlock一般表示玩家的一次操作,例如一次編隊、暫停游戲。其中部分操作要算入APM中,例如編隊,而有些操作不計算APM,例如暫停游戲。另外,還有的ActionBlock是自動生成的,也不算入APM。Action是否算入APM可以查看文檔w3g_actions.txt。

其中比較特殊的有ActionID為0x16的Action。這個Action表示選擇或取消選擇。ActionBlock的第二個字節為0x01表示選擇,0x02表示取消選擇。一般來說這個Action是算入APM的,但是如果兩個相鄰的ActionID為0x16的ActionBlock,前一個為取消選擇,后一個為選擇,那么這兩個ActionBlock只算一次有效的Action,因為前一個是自動生成的。

在游戲進行過程中,可能會有玩家暫停游戲的情況,也有玩家在游戲結束前提前退出游戲的情況。在計算APM的時候一定要去掉這部分的時間,這樣算出來的APM才準確。

下面的截圖就是RepKing錄像分析軟件沒有考慮游戲暫停導致的問題,導致玩家游戲時間大于錄像的時長,APM計算不準確。

Java解析Action和APM:

在Player.java中加入action表示玩家的有效操作數:

/**

* 操作次數

*/

private int action;

public int getAction() {

return action;

}

public void setAction(int action){

this.action = action;

}在ReplayData.java中,加入對Action的解析,在ReplayData中加入isPause表示游戲是否暫停,在游戲暫停時游戲時間不能增加:

/**

* 是否暫停

*/

private boolean isPause;

/**

* 解析一個時間塊

*/

private void analysisTimeSlot() {

offset++;

int bytes = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);

offset += 2;

// 游戲時間在非暫停狀態下增加

int timeIncrement = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);

if(!isPause) {

time += timeIncrement;

}

offset += 2;

// 解析Action

analysisAction(offset + bytes - 2);

}

/**

* 解析TimeSlot中的Action

* @param end TimeSlot的結束位置

*/

private void analysisAction(int timeSlotEnd) {

while(offset != timeSlotEnd) {

byte playerId = uncompressedDataBytes[offset];

Player player = getPlayById(playerId);

int action = player.getAction();

offset++;

int commandDataBlockbytes = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);

offset += 2;

int commandDataBlockEnd = offset + commandDataBlockbytes;

boolean lastActionWasDeselect = false;

while(offset != commandDataBlockEnd) {

byte actionId = uncompressedDataBytes[offset];

boolean thisActionIsDeselect = false;

if(actionId == 0x16 && uncompressedDataBytes[offset + 1] == 0x02) {

thisActionIsDeselect = true;

}

switch (actionId) {

// 暫停游戲

case 0x01:

isPause = true;

offset++;

break;

// 繼續游戲

case 0x02:

isPause = false;

offset++;

break;

case 0x03:

offset += 2;

break;

case 0x04:

case 0x05:

offset++;

break;

case 0x06:

offset++;

while(uncompressedDataBytes[offset] != 0) {

offset++;

}

offset++;

break;

case 0x07:

offset += 5;

break;

case 0x10:

offset += 15;

action++;

break;

case 0x11:

offset += 23;

action++;

break;

case 0x12:

offset += 31;

action++;

break;

case 0x13:

offset += 39;

action++;

break;

case 0x14:

offset += 44;

action++;

break;

case 0x16:

offset++;

byte selectMode = uncompressedDataBytes[offset];

offset++;

if(selectMode == 0x02) {

action++;

} else {

if(!lastActionWasDeselect) {

action++;

}

}

int number = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);

offset += 2;

offset += number * 8;

break;

case 0X17:

offset += 2;

int n = LittleEndianTool.getUnsignedInt16(uncompressedDataBytes, offset);

offset += 2;

offset += n * 8;

action++;

break;

case 0x18:

offset += 3;

action++;

break;

case 0x19:

offset += 13;

break;

case 0x1a:

offset++;

break;

case 0x1b:

offset += 10;

break;

case 0x1c:

offset += 10;

action++;

break;

case 0x1d:

offset += 9;

action++;

break;

case 0x1e:

offset += 6;

action++;

break;

case 0x21:

offset += 9;

break;

case 0x20:

case 0x22:

case 0x23:

case 0x24:

case 0x25:

case 0x26:

case 0x29:

case 0x2a:

case 0x2b:

case 0x2c:

case 0x2f:

case 0x30:

case 0x31:

case 0x32:

offset++;

break;

case 0x27:

case 0x28:

case 0x2d:

offset += 6;

break;

case 0x2e:

offset += 5;

break;

case 0x50:

offset += 6;

break;

case 0x51:

offset += 10;

break;

case 0x60:

offset += 9;

while(uncompressedDataBytes[offset] != 0) {

offset++;

}

offset++;

break;

case 0x61:

offset++;

action++;

break;

case 0x62:

offset += 13;

break;

case 0x66:

case 0x67:

offset++;

action++;

break;

case 0x68:

offset += 13;

break;

case 0x69:

case 0x6a:

offset += 17;

break;

case 0x75:

offset += 2;

break;

}

lastActionWasDeselect = thisActionIsDeselect;

}

player.setAction(action);

}

}在Test.java中,輸出計算得到的玩家APM值:

package com.xxg.w3gparser;

import java.io.File;

import java.io.IOException;

import java.util.List;

import java.util.zip.DataFormatException;

public class Test {

public static void main(String[] args) throws IOException, W3GException, DataFormatException {

Replay replay = new Replay(new File("C:/Documents and Settings/Administrator/桌面/131224_[UD]crabby_VS_[ORC]LuciferLVZ_LostTemple_RN.w3g"));

Header header = replay.getHeader();

System.out.println("版本:1." + header.getVersionNumber() + "." + header.getBuildNumber());

long duration = header.getDuration();

System.out.println("時長:" + convertMillisecondToString(duration));

UncompressedData uncompressedData = replay.getUncompressedData();

System.out.println("游戲名稱:" + uncompressedData.getGameName());

System.out.println("游戲創建者:" + uncompressedData.getCreaterName());

System.out.println("游戲地圖:" + uncompressedData.getMap());

List list = uncompressedData.getPlayerList();

for(Player player : list) {

System.out.println("---玩家" + player.getPlayerId() + "---");

System.out.println("玩家名稱:" + player.getPlayerName());

if(player.isHost()) {

System.out.println("是否主機:主機");

} else {

System.out.println("是否主機:否");

}

System.out.println("游戲時間:" + convertMillisecondToString(player.getPlayTime()));

System.out.println("操作次數:" + player.getAction());

System.out.println("APM:" + player.getAction() * 60000 / player.getPlayTime());

if(player.getTeamNumber() != 12) {

System.out.println("玩家隊伍:" + (player.getTeamNumber() + 1));

switch(player.getRace()) {

case 0x01:

case 0x41:

System.out.println("玩家種族:人族");

break;

case 0x02:

case 0x42:

System.out.println("玩家種族:獸族");

break;

case 0x04:

case 0x44:

System.out.println("玩家種族:暗夜精靈");

break;

case 0x08:

case 0x48:

System.out.println("玩家種族:不死族");

break;

case 0x20:

case 0x60:

System.out.println("玩家種族:隨機");

break;

}

switch(player.getColor()) {

case 0:

System.out.println("玩家顏色:紅");

break;

case 1:

System.out.println("玩家顏色:藍");

break;

case 2:

System.out.println("玩家顏色:青");

break;

case 3:

System.out.println("玩家顏色:紫");

break;

case 4:

System.out.println("玩家顏色:黃");

break;

case 5:

System.out.println("玩家顏色:橘");

break;

case 6:

System.out.println("玩家顏色:綠");

break;

case 7:

System.out.println("玩家顏色:粉");

break;

case 8:

System.out.println("玩家顏色:灰");

break;

case 9:

System.out.println("玩家顏色:淺藍");

break;

case 10:

System.out.println("玩家顏色:深綠");

break;

case 11:

System.out.println("玩家顏色:棕");

break;

}

System.out.println("障礙(血量):" + player.getHandicap() + "%");

if(player.isComputer()) {

System.out.println("是否電腦玩家:電腦玩家");

switch (player.getAiStrength())

{

case 0:

System.out.println("電腦難度:簡單的");

break;

case 1:

System.out.println("電腦難度:中等難度的");

break;

case 2:

System.out.println("電腦難度:令人發狂的");

break;

}

} else {

System.out.println("是否電腦玩家:否");

}

} else {

System.out.println("玩家隊伍:裁判或觀看者");

}

}

List chatList = uncompressedData.getReplayData().getChatList();

for(ChatMessage chatMessage : chatList) {

String chatString = "[" + convertMillisecondToString(chatMessage.getTime()) + "]";

chatString += chatMessage.getFrom().getPlayerName() + " 對 ";

switch ((int)chatMessage.getMode()) {

case 0:

chatString += "所有人";

break;

case 1:

chatString += "隊伍";

break;

case 2:

chatString += "裁判或觀看者";

break;

default:

chatString += chatMessage.getTo().getPlayerName();

}

chatString += " 說:" + chatMessage.getMessage();

System.out.println(chatString);

}

}

private static String convertMillisecondToString(long millisecond) {

long second = (millisecond / 1000) % 60;

long minite = (millisecond / 1000) / 60;

if (second < 10) {

return minite + ":0" + second;

} else {

return minite + ":" + second;

}

}

}輸出:

版本:1.26.6059

時長:15:39

游戲名稱:當地局域網內的游戲 (96

游戲創建者:962030958

游戲地圖:Maps\E-WCLMAP\(2)AncientIsles.w3x

---玩家1---

玩家名稱:962030958

是否主機:主機

游戲時間:15:38

操作次數:2635

APM:168玩家隊伍:1

玩家種族:不死族

玩家顏色:黃

障礙(血量):100%

是否電腦玩家:否

---玩家2---

玩家名稱:flygogogo

是否主機:否

游戲時間:15:37

操作次數:3483

APM:222玩家隊伍:2

玩家種族:獸族

玩家顏色:藍

障礙(血量):100%

是否電腦玩家:否

[0:12]962030958 對 所有人 說:glgl

[4:53]962030958 對 所有人 說:==

[4:53]962030958 對 所有人 說:貓咪爬到鍵盤上了

[4:53]962030958 對 所有人 說:g?

[4:53]flygogogo 對 所有人 說:g

[15:32]flygogogo 對 所有人 說:

[15:35]flygogogo 對 所有人 說:gg

[15:36]flygogogo 對 所有人 說:

結束語:

《Java解析魔獸爭霸3錄像W3G文件》系列博文就寫到這里了,當然還有很多可以繼續寫的東西,例如判斷玩家勝負,判斷玩家的英雄、單位等。需要源碼的同學可以在評論中留下E-mail。

總結

以上是生活随笔為你收集整理的java解析魔兽争霸3录像_Java解析魔兽争霸3录像W3G文件(五):Action和APM计算的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。