Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c58e690a47 | ||
|
1593965950 | ||
|
2a1105efa4 | ||
|
04de4666d5 | ||
|
2290c57c1c | ||
|
24d5ad9dc8 | ||
|
fdc296531a | ||
|
becc56f672 | ||
|
58c18ab7ae | ||
|
024d527f3d | ||
|
9b29edaaa1 | ||
|
360f2fc22b | ||
|
2dbc529978 | ||
|
d21f59cafa | ||
|
335fef66ef | ||
|
52480ab29f | ||
|
971f6461ab | ||
|
061aa449c9 | ||
|
78dd9ecd85 | ||
|
d4226137b6 | ||
|
e432026fec | ||
|
3e7718ed04 | ||
|
b78dd54431 | ||
|
22fb72afbc | ||
|
7b9172c27b | ||
|
7e12853a73 |
@@ -2,9 +2,10 @@
|
||||
|
||||
This project is a demo for a websocket-based rollback netcode inspired by [GGPO](https://github.com/pond3r/ggpo/blob/master/doc/README.md).
|
||||
|
||||

|
||||
_(the following gif is sped up to 2x for file size reduction)_
|
||||

|
||||
|
||||
Please also checkout [this demo video](https://pan.baidu.com/s/1YkfuHjNLzlFVnKiEj6wrDQ?pwd=tkr5) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||
Please also checkout [this demo video](https://pan.baidu.com/s/1fy0CuFKnVP_Gn2cDfrj6yg?pwd=q5uc) to see how this demo carries out a full 60fps synchronization with the help of _batched input upsync/downsync_ for satisfying network I/O performance.
|
||||
|
||||
The video mainly shows the following features.
|
||||
- The backend receives inputs from frontend peers and broadcasts back for synchronization.
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
. "battle_srv/common"
|
||||
"battle_srv/common/utils"
|
||||
"battle_srv/models"
|
||||
. "battle_srv/protos"
|
||||
"battle_srv/storage"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
@@ -526,8 +527,10 @@ func (p *playerController) maybeCreatePlayerWechatAuthBinding(userInfo utils.Use
|
||||
}
|
||||
if player != nil {
|
||||
updateInfo := models.Player{
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
DisplayName: userInfo.Nickname,
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
DisplayName: userInfo.Nickname,
|
||||
},
|
||||
}
|
||||
tx := storage.MySQLManagerIns.MustBegin()
|
||||
defer tx.Rollback()
|
||||
@@ -542,10 +545,12 @@ func (p *playerController) maybeCreatePlayerWechatAuthBinding(userInfo utils.Use
|
||||
}
|
||||
now := utils.UnixtimeMilli()
|
||||
player := models.Player{
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
DisplayName: userInfo.Nickname,
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
DisplayName: userInfo.Nickname,
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.Wechat))
|
||||
}
|
||||
@@ -562,8 +567,10 @@ func (p *playerController) maybeCreatePlayerWechatGameAuthBinding(userInfo utils
|
||||
}
|
||||
if player != nil {
|
||||
updateInfo := models.Player{
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
DisplayName: userInfo.Nickname,
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
DisplayName: userInfo.Nickname,
|
||||
},
|
||||
}
|
||||
tx := storage.MySQLManagerIns.MustBegin()
|
||||
defer tx.Rollback()
|
||||
@@ -578,10 +585,12 @@ func (p *playerController) maybeCreatePlayerWechatGameAuthBinding(userInfo utils
|
||||
}
|
||||
now := utils.UnixtimeMilli()
|
||||
player := models.Player{
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
DisplayName: userInfo.Nickname,
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
DisplayName: userInfo.Nickname,
|
||||
Avatar: userInfo.HeadImgURL,
|
||||
},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
return p.createNewPlayer(player, userInfo.OpenID, int(Constants.AuthChannel.WechatGame))
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
. "battle_srv/common"
|
||||
"battle_srv/common/utils"
|
||||
"battle_srv/models"
|
||||
. "battle_srv/protos"
|
||||
"battle_srv/storage"
|
||||
. "dnmshared"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
@@ -71,7 +72,6 @@ func createMysqlData(rows *sqlx.Rows, v string) {
|
||||
}
|
||||
}
|
||||
|
||||
// 加上tableName参数, 用于pre_conf_data.sqlite里bot_player表的复用 --kobako
|
||||
func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
||||
var ls []*dbBotPlayer
|
||||
err := db.Select(&ls, "SELECT name, magic_phone_country_code, magic_phone_num, display_name FROM "+tableName)
|
||||
@@ -88,7 +88,6 @@ func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
||||
panic(err)
|
||||
}
|
||||
query = storage.MySQLManagerIns.Rebind(query)
|
||||
// existNames := make([]string, len(ls), len(ls))
|
||||
var existPlayers []*models.Player
|
||||
err = storage.MySQLManagerIns.Select(&existPlayers, query, args...)
|
||||
if nil != err {
|
||||
@@ -99,13 +98,11 @@ func maybeCreateNewPlayerFromBotTable(db *sqlx.DB, tableName string) {
|
||||
var flag bool
|
||||
for _, v := range existPlayers {
|
||||
if botPlayer.Name == v.Name {
|
||||
// 已有数据,合并处理
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
// 找不到,新增
|
||||
Logger.Debug("create", zap.Any(tableName, botPlayer))
|
||||
err := createNewBotPlayer(botPlayer)
|
||||
if err != nil {
|
||||
@@ -120,11 +117,14 @@ func createNewBotPlayer(p *dbBotPlayer) error {
|
||||
defer tx.Rollback()
|
||||
now := utils.UnixtimeMilli()
|
||||
player := models.Player{
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Name: p.Name,
|
||||
DisplayName: p.DisplayName,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
Name: p.Name,
|
||||
DisplayName: p.DisplayName,
|
||||
},
|
||||
}
|
||||
|
||||
err := player.Insert(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
. "battle_srv/common"
|
||||
"battle_srv/common/utils"
|
||||
"battle_srv/models"
|
||||
. "battle_srv/protos"
|
||||
"battle_srv/storage"
|
||||
. "dnmshared"
|
||||
|
||||
@@ -75,9 +76,11 @@ func createNewPlayer(p *dbTestPlayer) error {
|
||||
defer tx.Rollback()
|
||||
now := utils.UnixtimeMilli()
|
||||
player := models.Player{
|
||||
PlayerDownsync: PlayerDownsync{
|
||||
Name: p.Name,
|
||||
},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Name: p.Name,
|
||||
}
|
||||
err := player.Insert(tx)
|
||||
if err != nil {
|
||||
|
@@ -2,10 +2,9 @@ package models
|
||||
|
||||
import (
|
||||
. "battle_srv/protos"
|
||||
. "dnmshared/sharedprotos"
|
||||
)
|
||||
|
||||
func toPbPlayers(modelInstances map[int32]*Player) map[int32]*PlayerDownsync {
|
||||
func toPbPlayers(modelInstances map[int32]*Player, withMetaInfo bool) map[int32]*PlayerDownsync {
|
||||
toRet := make(map[int32]*PlayerDownsync, 0)
|
||||
if nil == modelInstances {
|
||||
return toRet
|
||||
@@ -13,18 +12,22 @@ func toPbPlayers(modelInstances map[int32]*Player) map[int32]*PlayerDownsync {
|
||||
|
||||
for k, last := range modelInstances {
|
||||
toRet[k] = &PlayerDownsync{
|
||||
Id: last.Id,
|
||||
VirtualGridX: last.VirtualGridX,
|
||||
VirtualGridY: last.VirtualGridY,
|
||||
Dir: &Direction{
|
||||
Dx: last.Dir.Dx,
|
||||
Dy: last.Dir.Dy,
|
||||
},
|
||||
Speed: last.Speed,
|
||||
BattleState: last.BattleState,
|
||||
Score: last.Score,
|
||||
Removed: last.Removed,
|
||||
JoinIndex: last.JoinIndex,
|
||||
Id: last.Id,
|
||||
VirtualGridX: last.VirtualGridX,
|
||||
VirtualGridY: last.VirtualGridY,
|
||||
DirX: last.DirX,
|
||||
DirY: last.DirY,
|
||||
ColliderRadius: last.ColliderRadius,
|
||||
Speed: last.Speed,
|
||||
BattleState: last.BattleState,
|
||||
Score: last.Score,
|
||||
Removed: last.Removed,
|
||||
JoinIndex: last.JoinIndex,
|
||||
}
|
||||
if withMetaInfo {
|
||||
toRet[k].Name = last.Name
|
||||
toRet[k].DisplayName = last.DisplayName
|
||||
toRet[k].Avatar = last.Avatar
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,13 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
. "dnmshared/sharedprotos"
|
||||
. "battle_srv/protos"
|
||||
"battle_srv/storage"
|
||||
. "dnmshared"
|
||||
"fmt"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type PlayerBattleState struct {
|
||||
@@ -33,12 +35,7 @@ func InitPlayerBattleStateIns() {
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
// Meta info fields
|
||||
Id int32 `json:"id,omitempty" db:"id"`
|
||||
Name string `json:"name,omitempty" db:"name"`
|
||||
DisplayName string `json:"displayName,omitempty" db:"display_name"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
ColliderRadius float64 `json:"-"`
|
||||
PlayerDownsync
|
||||
|
||||
// DB only fields
|
||||
CreatedAt int64 `db:"created_at"`
|
||||
@@ -46,19 +43,10 @@ type Player struct {
|
||||
DeletedAt NullInt64 `db:"deleted_at"`
|
||||
TutorialStage int `db:"tutorial_stage"`
|
||||
|
||||
// in-battle info fields
|
||||
VirtualGridX int32
|
||||
VirtualGridY int32
|
||||
Dir *Direction
|
||||
Speed int32
|
||||
BattleState int32
|
||||
LastMoveGmtMillis int32
|
||||
Score int32
|
||||
Removed bool
|
||||
JoinIndex int32
|
||||
// other in-battle info fields
|
||||
LastSentInputFrameId int32
|
||||
AckingFrameId int32
|
||||
AckingInputFrameId int32
|
||||
LastSentInputFrameId int32
|
||||
}
|
||||
|
||||
func ExistPlayerByName(name string) (bool, error) {
|
||||
@@ -74,15 +62,43 @@ func GetPlayerById(id int) (*Player, error) {
|
||||
}
|
||||
|
||||
func getPlayer(cond sq.Eq) (*Player, error) {
|
||||
var p Player
|
||||
err := getObj("player", cond, &p)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
p := Player{}
|
||||
pd := PlayerDownsync{}
|
||||
query, args, err := sq.Select("*").From("player").Where(cond).Limit(1).ToSql()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Dir = &Direction{
|
||||
Dx: 0,
|
||||
Dy: 0,
|
||||
rows, err := storage.MySQLManagerIns.Queryx(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cols, err := rows.Columns()
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
for rows.Next() {
|
||||
// TODO: Do it more elegantly, but by now I don't have time to learn reflection of Golang
|
||||
vals := rowValues(rows, cols)
|
||||
for i, col := range cols {
|
||||
val := *vals[i].(*interface{})
|
||||
if "id" == col {
|
||||
pd.Id = int32(val.(int64))
|
||||
}
|
||||
if "name" == col {
|
||||
switch v := val.(type) {
|
||||
case []byte:
|
||||
pd.Name = string(v)
|
||||
default:
|
||||
pd.Name = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
if "created_at" == col {
|
||||
p.CreatedAt = int64(val.(int64))
|
||||
}
|
||||
}
|
||||
Logger.Info("Queried player from db", zap.Any("cond", cond), zap.Any("p", p), zap.Any("pd", pd), zap.Any("cols", cols), zap.Any("rowValues", vals))
|
||||
}
|
||||
p.PlayerDownsync = pd
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
@@ -113,8 +129,6 @@ func Update(tx *sqlx.Tx, id int32, p *Player) (bool, error) {
|
||||
}
|
||||
result, err := tx.Exec(query, args...)
|
||||
if err != nil {
|
||||
fmt.Println("ERRRRRRR: ")
|
||||
fmt.Println(err)
|
||||
return false, err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
|
@@ -10,6 +10,17 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func rowValues(rows *sqlx.Rows, cols []string) []interface{} {
|
||||
results := make([]interface{}, len(cols))
|
||||
for i := range results {
|
||||
results[i] = new(interface{})
|
||||
}
|
||||
if err := rows.Scan(results[:]...); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func exist(t string, cond sq.Eq) (bool, error) {
|
||||
c, err := getCount(t, cond)
|
||||
if err != nil {
|
||||
|
@@ -54,6 +54,7 @@ const (
|
||||
|
||||
COLLISION_PLAYER_INDEX_PREFIX = (1 << 17)
|
||||
COLLISION_BARRIER_INDEX_PREFIX = (1 << 16)
|
||||
COLLISION_BULLET_INDEX_PREFIX = (1 << 15)
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -61,6 +62,17 @@ const (
|
||||
MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED = -2
|
||||
)
|
||||
|
||||
const (
|
||||
ATK_CHARACTER_STATE_IDLE1 = 0
|
||||
ATK_CHARACTER_STATE_WALKING = 1
|
||||
ATK_CHARACTER_STATE_ATK1 = 2
|
||||
ATK_CHARACTER_STATE_ATKED1 = 3
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_PLAYER_RADIUS = float64(16)
|
||||
)
|
||||
|
||||
// These directions are chosen such that when speed is changed to "(speedX+delta, speedY+delta)" for any of them, the direction is unchanged.
|
||||
var DIRECTION_DECODER = [][]int32{
|
||||
{0, 0},
|
||||
@@ -116,6 +128,7 @@ type Room struct {
|
||||
collisionSpaceOffsetY float64
|
||||
Players map[int32]*Player
|
||||
PlayersArr []*Player // ordered by joinIndex
|
||||
Space *resolv.Space
|
||||
CollisionSysMap map[int32]*resolv.Object
|
||||
/**
|
||||
* The following `PlayerDownsyncSessionDict` is NOT individually put
|
||||
@@ -142,11 +155,6 @@ type Room struct {
|
||||
Index int
|
||||
RenderFrameId int32
|
||||
CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback
|
||||
ServerFps int32
|
||||
BattleDurationFrames int32
|
||||
BattleDurationNanos int64
|
||||
InputFrameUpsyncDelayTolerance int32
|
||||
MaxChasingRenderFramesPerUpdate int32
|
||||
EffectivePlayerCount int32
|
||||
DismissalWaitGroup sync.WaitGroup
|
||||
Barriers map[int32]*Barrier
|
||||
@@ -156,27 +164,15 @@ type Room struct {
|
||||
LastAllConfirmedInputFrameId int32
|
||||
LastAllConfirmedInputFrameIdWithChange int32
|
||||
LastAllConfirmedInputList []uint64
|
||||
InputDelayFrames int32 // in the count of render frames
|
||||
NstDelayFrames int32 // network-single-trip delay in the count of render frames, proposed to be (InputDelayFrames >> 1) because we expect a round-trip delay to be exactly "InputDelayFrames"
|
||||
InputScaleFrames uint32 // inputDelayedAndScaledFrameId = ((originalFrameId - InputDelayFrames) >> InputScaleFrames)
|
||||
JoinIndexBooleanArr []bool
|
||||
RollbackEstimatedDtMillis float64
|
||||
RollbackEstimatedDtNanos int64
|
||||
LastRenderFrameIdTriggeredAt int64
|
||||
|
||||
WorldToVirtualGridRatio float64
|
||||
VirtualGridToWorldRatio float64
|
||||
BackendDynamicsEnabled bool
|
||||
LastRenderFrameIdTriggeredAt int64
|
||||
PlayerDefaultSpeed int32
|
||||
|
||||
PlayerDefaultSpeed int32
|
||||
|
||||
StageName string
|
||||
StageDiscreteW int32
|
||||
StageDiscreteH int32
|
||||
StageTileW int32
|
||||
StageTileH int32
|
||||
RawBattleStrToVec2DListMap StrToVec2DListMap
|
||||
RawBattleStrToPolygon2DListMap StrToPolygon2DListMap
|
||||
BackendDynamicsEnabled bool
|
||||
BulletBattleLocalIdCounter int32
|
||||
dilutedRollbackEstimatedDtNanos int64
|
||||
BattleColliderInfo // Compositing to send centralized magic numbers
|
||||
}
|
||||
|
||||
func (pR *Room) updateScore() {
|
||||
@@ -200,8 +196,8 @@ func (pR *Room) AddPlayerIfPossible(pPlayerFromDbInit *Player, session *websocke
|
||||
pPlayerFromDbInit.AckingInputFrameId = -1
|
||||
pPlayerFromDbInit.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_NORMAL_ADDED
|
||||
pPlayerFromDbInit.BattleState = PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK
|
||||
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pPlayerFromDbInit.ColliderRadius = float64(24) // Hardcoded
|
||||
pPlayerFromDbInit.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pPlayerFromDbInit.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||
|
||||
pR.Players[playerId] = pPlayerFromDbInit
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
@@ -226,15 +222,16 @@ func (pR *Room) ReAddPlayerIfPossible(pTmpPlayerInstance *Player, session *webso
|
||||
* -- YFLu
|
||||
*/
|
||||
defer pR.onPlayerReAdded(playerId)
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
||||
pEffectiveInRoomPlayerInstance := pR.Players[playerId]
|
||||
pEffectiveInRoomPlayerInstance.AckingFrameId = -1
|
||||
pEffectiveInRoomPlayerInstance.AckingInputFrameId = -1
|
||||
pEffectiveInRoomPlayerInstance.LastSentInputFrameId = MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED
|
||||
pEffectiveInRoomPlayerInstance.BattleState = PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK
|
||||
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.ColliderRadius = float64(16) // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.Speed = pR.PlayerDefaultSpeed // Hardcoded
|
||||
pEffectiveInRoomPlayerInstance.ColliderRadius = DEFAULT_PLAYER_RADIUS // Hardcoded
|
||||
|
||||
pR.PlayerDownsyncSessionDict[playerId] = session
|
||||
pR.PlayerSignalToCloseDict[playerId] = signalToCloseConnOfThisPlayer
|
||||
|
||||
Logger.Warn("ReAddPlayerIfPossible finished.", zap.Any("roomId", pR.Id), zap.Any("playerId", playerId), zap.Any("joinIndex", pEffectiveInRoomPlayerInstance.JoinIndex), zap.Any("playerBattleState", pEffectiveInRoomPlayerInstance.BattleState), zap.Any("roomState", pR.State), zap.Any("roomEffectivePlayerCount", pR.EffectivePlayerCount), zap.Any("AckingFrameId", pEffectiveInRoomPlayerInstance.AckingFrameId), zap.Any("AckingInputFrameId", pEffectiveInRoomPlayerInstance.AckingInputFrameId), zap.Any("LastSentInputFrameId", pEffectiveInRoomPlayerInstance.LastSentInputFrameId))
|
||||
return true
|
||||
@@ -252,7 +249,7 @@ func (pR *Room) ChooseStage() error {
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().Unix())
|
||||
stageNameList := []string{"simple" /* "richsoil" */}
|
||||
stageNameList := []string{"dungeon" /*"dungeon", "simple", "richsoil" */}
|
||||
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
|
||||
|
||||
pR.StageName = stageNameList[chosenStageIndex]
|
||||
@@ -277,8 +274,8 @@ func (pR *Room) ChooseStage() error {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Obtain the content of `gidBoundariesMapInB2World`.
|
||||
gidBoundariesMapInB2World := make(map[int]StrToPolygon2DListMap, 0)
|
||||
// Obtain the content of `gidBoundariesMap`.
|
||||
gidBoundariesMap := make(map[int]StrToPolygon2DListMap, 0)
|
||||
for _, tileset := range pTmxMapIns.Tilesets {
|
||||
relativeTsxFilePath := fmt.Sprintf("%s/%s", filepath.Join(pwd, relativePathForChosenStage), tileset.Source) // Note that "TmxTileset.Source" can be a string of "relative path".
|
||||
absTsxFilePath, err := filepath.Abs(relativeTsxFilePath)
|
||||
@@ -294,10 +291,10 @@ func (pR *Room) ChooseStage() error {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
DeserializeTsxToColliderDict(pTmxMapIns, byteArrOfTsxFile, int(tileset.FirstGid), gidBoundariesMapInB2World)
|
||||
DeserializeTsxToColliderDict(pTmxMapIns, byteArrOfTsxFile, int(tileset.FirstGid), gidBoundariesMap)
|
||||
}
|
||||
|
||||
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, toRetStrToVec2DListMap, toRetStrToPolygon2DListMap, err := ParseTmxLayersAndGroups(pTmxMapIns, gidBoundariesMapInB2World)
|
||||
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, strToVec2DListMap, strToPolygon2DListMap, err := ParseTmxLayersAndGroups(pTmxMapIns, gidBoundariesMap)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
}
|
||||
@@ -306,10 +303,10 @@ func (pR *Room) ChooseStage() error {
|
||||
pR.StageDiscreteH = stageDiscreteH
|
||||
pR.StageTileW = stageTileW
|
||||
pR.StageTileH = stageTileH
|
||||
pR.RawBattleStrToVec2DListMap = toRetStrToVec2DListMap
|
||||
pR.RawBattleStrToPolygon2DListMap = toRetStrToPolygon2DListMap
|
||||
pR.StrToVec2DListMap = strToVec2DListMap
|
||||
pR.StrToPolygon2DListMap = strToPolygon2DListMap
|
||||
|
||||
barrierPolygon2DList := *(toRetStrToPolygon2DListMap["Barrier"])
|
||||
barrierPolygon2DList := *(strToPolygon2DListMap["Barrier"])
|
||||
|
||||
var barrierLocalIdInBattle int32 = 0
|
||||
for _, polygon2DUnaligned := range barrierPolygon2DList.Eles {
|
||||
@@ -345,13 +342,6 @@ func (pR *Room) ConvertToLastUsedRenderFrameId(inputFrameId int32, inputDelayFra
|
||||
return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames + (1 << pR.InputScaleFrames) - 1)
|
||||
}
|
||||
|
||||
func (pR *Room) EncodeUpsyncCmd(upsyncCmd *InputFrameUpsync) uint64 {
|
||||
var ret uint64 = 0
|
||||
// There're 13 possible directions, occupying the first 4 bits, no need to shift
|
||||
ret += uint64(upsyncCmd.EncodedDir)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (pR *Room) RenderFrameBufferString() string {
|
||||
return fmt.Sprintf("{renderFrameId: %d, stRenderFrameId: %d, edRenderFrameId: %d, lastAllConfirmedRenderFrameId: %d}", pR.RenderFrameId, pR.RenderFrameBuffer.StFrameId, pR.RenderFrameBuffer.EdFrameId, pR.CurDynamicsRenderFrameId)
|
||||
}
|
||||
@@ -370,6 +360,7 @@ func (pR *Room) InputsBufferString(allDetails bool) string {
|
||||
break
|
||||
}
|
||||
f := tmp.(*InputFrameDownsync)
|
||||
//s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, &inputList: %p, confirmedList: %v}", f.InputFrameId, f.InputList, &(f.InputList), f.ConfirmedList))
|
||||
s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList))
|
||||
}
|
||||
|
||||
@@ -385,15 +376,13 @@ func (pR *Room) StartBattle() {
|
||||
return
|
||||
}
|
||||
|
||||
// Always instantiates a new channel and let the old one die out due to not being retained by any root reference.
|
||||
nanosPerFrame := 1000000000 / int64(pR.ServerFps)
|
||||
pR.RenderFrameId = 0
|
||||
|
||||
// Initialize the "collisionSys" as well as "RenderFrameBuffer"
|
||||
pR.CurDynamicsRenderFrameId = 0
|
||||
kickoffFrame := &RoomDownsyncFrame{
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
Players: toPbPlayers(pR.Players, false),
|
||||
CountdownNanos: pR.BattleDurationNanos,
|
||||
}
|
||||
pR.RenderFrameBuffer.Put(kickoffFrame)
|
||||
@@ -426,7 +415,7 @@ func (pR *Room) StartBattle() {
|
||||
stCalculation := utils.UnixtimeNano()
|
||||
|
||||
elapsedNanosSinceLastFrameIdTriggered := stCalculation - pR.LastRenderFrameIdTriggeredAt
|
||||
if elapsedNanosSinceLastFrameIdTriggered < pR.RollbackEstimatedDtNanos {
|
||||
if elapsedNanosSinceLastFrameIdTriggered < pR.dilutedRollbackEstimatedDtNanos {
|
||||
Logger.Debug(fmt.Sprintf("Avoiding too fast frame@roomId=%v, renderFrameId=%v: elapsedNanosSinceLastFrameIdTriggered=%v", pR.Id, pR.RenderFrameId, elapsedNanosSinceLastFrameIdTriggered))
|
||||
continue
|
||||
}
|
||||
@@ -537,8 +526,8 @@ func (pR *Room) StartBattle() {
|
||||
2. reconnection
|
||||
*/
|
||||
shouldResync1 := (MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId)
|
||||
// shouldResync2 := (0 < (unconfirmedMask & uint64(1 << uint32(player.JoinIndex-1)))) // This condition is critical, if we don't send resync upon this condition, the "reconnected or slowly-clocking player" might never get its input synced
|
||||
shouldResync2 := (0 < unconfirmedMask) // An easier version of the above, might keep sending "refRenderFrame"s to still connected players when any player is disconnected
|
||||
shouldResync2 := (0 < (unconfirmedMask & uint64(1<<uint32(player.JoinIndex-1)))) // This condition is critical, if we don't send resync upon this condition, the "reconnected or slowly-clocking player" might never get its input synced
|
||||
// shouldResync2 := (0 < unconfirmedMask) // An easier version of the above, might keep sending "refRenderFrame"s to still connected players when any player is disconnected
|
||||
if pR.BackendDynamicsEnabled && (shouldResync1 || shouldResync2) {
|
||||
tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId)
|
||||
if nil == tmp {
|
||||
@@ -560,35 +549,38 @@ func (pR *Room) StartBattle() {
|
||||
}
|
||||
}
|
||||
|
||||
toApplyInputFrameId := pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames)
|
||||
minToKeepInputFrameId := pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames) - pR.SpAtkLookupFrames
|
||||
/*
|
||||
[WARNING]
|
||||
The following updates to "toApplyInputFrameId" is necessary because when "false == pR.BackendDynamicsEnabled", the variable "refRenderFrameId" is not well defined.
|
||||
The following updates to "minToKeepInputFrameId" is necessary because when "false == pR.BackendDynamicsEnabled", the variable "refRenderFrameId" is not well defined.
|
||||
*/
|
||||
minLastSentInputFrameId := int32(math.MaxInt32)
|
||||
for _, player := range pR.Players {
|
||||
if PlayerBattleStateIns.ACTIVE != player.BattleState {
|
||||
continue
|
||||
}
|
||||
if player.LastSentInputFrameId >= minLastSentInputFrameId {
|
||||
continue
|
||||
}
|
||||
minLastSentInputFrameId = player.LastSentInputFrameId
|
||||
}
|
||||
if minLastSentInputFrameId < toApplyInputFrameId {
|
||||
toApplyInputFrameId = minLastSentInputFrameId
|
||||
if minLastSentInputFrameId < minToKeepInputFrameId {
|
||||
minToKeepInputFrameId = minLastSentInputFrameId
|
||||
}
|
||||
for pR.InputsBuffer.N < pR.InputsBuffer.Cnt || (0 < pR.InputsBuffer.Cnt && pR.InputsBuffer.StFrameId < toApplyInputFrameId) {
|
||||
for pR.InputsBuffer.N < pR.InputsBuffer.Cnt || (0 < pR.InputsBuffer.Cnt && pR.InputsBuffer.StFrameId < minToKeepInputFrameId) {
|
||||
f := pR.InputsBuffer.Pop().(*InputFrameDownsync)
|
||||
if pR.inputFrameIdDebuggable(f.InputFrameId) {
|
||||
// Popping of an "inputFrame" would be AFTER its being all being confirmed, because it requires the "inputFrame" to be all acked
|
||||
Logger.Debug("inputFrame lifecycle#4[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("toApplyInputFrameId", toApplyInputFrameId), zap.Any("InputsBuffer", pR.InputsBufferString(false)))
|
||||
Logger.Debug("inputFrame lifecycle#4[popped]:", zap.Any("roomId", pR.Id), zap.Any("inputFrameId", f.InputFrameId), zap.Any("minToKeepInputFrameId", minToKeepInputFrameId), zap.Any("InputsBuffer", pR.InputsBufferString(false)))
|
||||
}
|
||||
}
|
||||
|
||||
pR.RenderFrameId++
|
||||
elapsedInCalculation := (utils.UnixtimeNano() - stCalculation)
|
||||
if elapsedInCalculation > nanosPerFrame {
|
||||
Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, expected nanosPerFrame=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, nanosPerFrame))
|
||||
if elapsedInCalculation > pR.dilutedRollbackEstimatedDtNanos {
|
||||
Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, dilutedRollbackEstimatedDtNanos=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, pR.dilutedRollbackEstimatedDtNanos))
|
||||
}
|
||||
time.Sleep(time.Duration(nanosPerFrame - elapsedInCalculation))
|
||||
time.Sleep(time.Duration(pR.dilutedRollbackEstimatedDtNanos - elapsedInCalculation))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +624,6 @@ func (pR *Room) OnBattleCmdReceived(pReq *WsReq) {
|
||||
Logger.Debug(fmt.Sprintf("Omitting obsolete inputFrameUpsync: roomId=%v, playerId=%v, clientInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.InputsBufferString(false)))
|
||||
continue
|
||||
}
|
||||
|
||||
bufIndex := pR.toDiscreteInputsBufferIndex(clientInputFrameId, pReq.JoinIndex)
|
||||
pR.DiscreteInputsBuffer.Store(bufIndex, inputFrameUpsync)
|
||||
|
||||
@@ -684,7 +675,7 @@ func (pR *Room) StopBattleForSettlement() {
|
||||
for playerId, _ := range pR.Players {
|
||||
assembledFrame := RoomDownsyncFrame{
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
Players: toPbPlayers(pR.Players, false),
|
||||
CountdownNanos: -1, // TODO: Replace this magic constant!
|
||||
}
|
||||
pR.sendSafely(&assembledFrame, nil, DOWNSYNC_MSG_ACT_BATTLE_STOPPED, playerId)
|
||||
@@ -708,22 +699,9 @@ func (pR *Room) onBattlePrepare(cb BattleStartCbType) {
|
||||
pR.State = RoomBattleStateIns.PREPARE
|
||||
Logger.Info("Battle state transitted to RoomBattleStateIns.PREPARE for:", zap.Any("roomId", pR.Id))
|
||||
|
||||
playerMetas := make(map[int32]*PlayerDownsyncMeta, 0)
|
||||
for _, player := range pR.Players {
|
||||
playerMetas[player.Id] = &PlayerDownsyncMeta{
|
||||
Id: player.Id,
|
||||
Name: player.Name,
|
||||
DisplayName: player.DisplayName,
|
||||
Avatar: player.Avatar,
|
||||
ColliderRadius: player.ColliderRadius, // hardcoded for now
|
||||
JoinIndex: player.JoinIndex,
|
||||
}
|
||||
}
|
||||
|
||||
battleReadyToStartFrame := &RoomDownsyncFrame{
|
||||
Id: DOWNSYNC_MSG_ACT_BATTLE_READY_TO_START,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
PlayerMetas: playerMetas,
|
||||
Players: toPbPlayers(pR.Players, true),
|
||||
CountdownNanos: pR.BattleDurationNanos,
|
||||
}
|
||||
|
||||
@@ -790,8 +768,10 @@ func (pR *Room) Dismiss() {
|
||||
func (pR *Room) OnDismissed() {
|
||||
|
||||
// Always instantiates new HeapRAM blocks and let the old blocks die out due to not being retained by any root reference.
|
||||
pR.BulletBattleLocalIdCounter = 0
|
||||
pR.WorldToVirtualGridRatio = float64(1000)
|
||||
pR.VirtualGridToWorldRatio = float64(1.0) / pR.WorldToVirtualGridRatio // this is a one-off computation, should avoid division in iterations
|
||||
pR.SpAtkLookupFrames = 5
|
||||
pR.PlayerDefaultSpeed = int32(float64(2) * pR.WorldToVirtualGridRatio) // in virtual grids per frame
|
||||
pR.Players = make(map[int32]*Player)
|
||||
pR.PlayersArr = make([]*Player, pR.Capacity)
|
||||
@@ -800,9 +780,10 @@ func (pR *Room) OnDismissed() {
|
||||
pR.PlayerSignalToCloseDict = make(map[int32]SignalToCloseConnCbType)
|
||||
pR.JoinIndexBooleanArr = make([]bool, pR.Capacity)
|
||||
pR.Barriers = make(map[int32]*Barrier)
|
||||
pR.InputsBuffer = NewRingBuffer(1024)
|
||||
pR.RenderCacheSize = 1024
|
||||
pR.RenderFrameBuffer = NewRingBuffer(pR.RenderCacheSize)
|
||||
pR.DiscreteInputsBuffer = sync.Map{}
|
||||
pR.RenderFrameBuffer = NewRingBuffer(1024)
|
||||
pR.InputsBuffer = NewRingBuffer((pR.RenderCacheSize >> 2) + 1)
|
||||
|
||||
pR.LastAllConfirmedInputFrameId = -1
|
||||
pR.LastAllConfirmedInputFrameIdWithChange = -1
|
||||
@@ -811,17 +792,45 @@ func (pR *Room) OnDismissed() {
|
||||
pR.RenderFrameId = 0
|
||||
pR.CurDynamicsRenderFrameId = 0
|
||||
pR.InputDelayFrames = 8
|
||||
pR.NstDelayFrames = 8
|
||||
pR.NstDelayFrames = 4
|
||||
pR.InputScaleFrames = uint32(2)
|
||||
pR.ServerFps = 60
|
||||
pR.RollbackEstimatedDtMillis = 16.667 // Use fixed-and-low-precision to mitigate the inconsistent floating-point-number issue between Golang and JavaScript
|
||||
pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for preventing FAST FRAME
|
||||
dilutionFactor := 12
|
||||
pR.dilutedRollbackEstimatedDtNanos = int64(16666666 * (dilutionFactor) / (dilutionFactor - 1)) // [WARNING] Only used in controlling "battleMainLoop" to be keep a frame rate lower than that of the frontends, such that upon resync(i.e. BackendDynamicsEnabled=true), the frontends would have bigger chances to keep up with or even surpass the backend calculation
|
||||
pR.BattleDurationFrames = 30 * pR.ServerFps
|
||||
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
||||
pR.InputFrameUpsyncDelayTolerance = 2
|
||||
pR.MaxChasingRenderFramesPerUpdate = 5
|
||||
|
||||
pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work!
|
||||
punchSkillId := int32(1)
|
||||
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)
|
||||
pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{
|
||||
// for offender
|
||||
StartupFrames: int32(23),
|
||||
ActiveFrames: int32(3),
|
||||
RecoveryFrames: int32(61), // I hereby set it to be 1 frame more than the actual animation to avoid critical transition, i.e. when the animation is 1 frame from ending but "rdfPlayer.framesToRecover" is already counted 0 and the player triggers an other same attack, making an effective bullet trigger but no animation is played due to same animName is still playing
|
||||
RecoveryFramesOnBlock: int32(61),
|
||||
RecoveryFramesOnHit: int32(61),
|
||||
Moveforward: &Vec2D{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
},
|
||||
HitboxOffset: float64(24.0), // should be about the radius of the PlayerCollider
|
||||
HitboxSize: &Vec2D{
|
||||
X: float64(45.0),
|
||||
Y: float64(32.0),
|
||||
},
|
||||
|
||||
// for defender
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Pushback: float64(11.0),
|
||||
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
|
||||
Damage: int32(5),
|
||||
}
|
||||
|
||||
pR.ChooseStage()
|
||||
pR.EffectivePlayerCount = 0
|
||||
@@ -931,7 +940,7 @@ func (pR *Room) onPlayerAdded(playerId int32) {
|
||||
pR.JoinIndexBooleanArr[index] = true
|
||||
|
||||
// Lazily assign the initial position of "Player" for "RoomDownsyncFrame".
|
||||
playerPosList := *(pR.RawBattleStrToVec2DListMap["PlayerStartingPos"])
|
||||
playerPosList := *(pR.StrToVec2DListMap["PlayerStartingPos"])
|
||||
if index > len(playerPosList.Eles) {
|
||||
panic(fmt.Sprintf("onPlayerAdded error, index >= len(playerPosList), roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
||||
}
|
||||
@@ -941,6 +950,14 @@ func (pR *Room) onPlayerAdded(playerId int32) {
|
||||
panic(fmt.Sprintf("onPlayerAdded error, nil == playerPos, roomId=%v, playerId=%v, roomState=%v, roomEffectivePlayerCount=%v", pR.Id, playerId, pR.State, pR.EffectivePlayerCount))
|
||||
}
|
||||
pR.Players[playerId].VirtualGridX, pR.Players[playerId].VirtualGridY = WorldToVirtualGridPos(playerPos.X, playerPos.Y, pR.WorldToVirtualGridRatio)
|
||||
// Hardcoded initial character orientation/facing
|
||||
if 0 == (pR.Players[playerId].JoinIndex % 2) {
|
||||
pR.Players[playerId].DirX = -2
|
||||
pR.Players[playerId].DirY = 0
|
||||
} else {
|
||||
pR.Players[playerId].DirX = +2
|
||||
pR.Players[playerId].DirY = 0
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
@@ -968,18 +985,6 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
playerMetas := make(map[int32]*PlayerDownsyncMeta, 0)
|
||||
for _, eachPlayer := range pR.Players {
|
||||
playerMetas[eachPlayer.Id] = &PlayerDownsyncMeta{
|
||||
Id: eachPlayer.Id,
|
||||
Name: eachPlayer.Name,
|
||||
DisplayName: eachPlayer.DisplayName,
|
||||
Avatar: eachPlayer.Avatar,
|
||||
JoinIndex: eachPlayer.JoinIndex,
|
||||
ColliderRadius: eachPlayer.ColliderRadius,
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast added or readded player info to all players in the same room
|
||||
for _, eachPlayer := range pR.Players {
|
||||
/*
|
||||
@@ -993,16 +998,14 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
|
||||
switch targetPlayer.BattleState {
|
||||
case PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK:
|
||||
playerAckedFrame := &RoomDownsyncFrame{
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
PlayerMetas: playerMetas,
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players, true),
|
||||
}
|
||||
pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, eachPlayer.Id)
|
||||
case PlayerBattleStateIns.READDED_PENDING_BATTLE_COLLIDER_ACK:
|
||||
playerAckedFrame := &RoomDownsyncFrame{
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players),
|
||||
PlayerMetas: playerMetas,
|
||||
Id: pR.RenderFrameId,
|
||||
Players: toPbPlayers(pR.Players, true),
|
||||
}
|
||||
pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_READDED_AND_ACKED, eachPlayer.Id)
|
||||
default:
|
||||
@@ -1073,12 +1076,15 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *InputFrameDownsync
|
||||
ConfirmedList: uint64(0),
|
||||
}
|
||||
} else {
|
||||
tmp := pR.InputsBuffer.GetByFrameId(inputFrameId - 1)
|
||||
tmp := pR.InputsBuffer.GetByFrameId(inputFrameId - 1) // There's no need for the backend to find the "lastAllConfirmed inputs" for prefabbing, either "BackendDynamicsEnabled" is true or false
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("Error prefabbing inputFrameDownsync: roomId=%v, InputsBuffer=%v", pR.Id, pR.InputsBufferString(false)))
|
||||
}
|
||||
prevInputFrameDownsync := tmp.(*InputFrameDownsync)
|
||||
currInputList := prevInputFrameDownsync.InputList // Would be a clone of the values
|
||||
currInputList := make([]uint64, pR.Capacity) // Would be a clone of the values
|
||||
for i, _ := range currInputList {
|
||||
currInputList[i] = (prevInputFrameDownsync.InputList[i] & uint64(15)) // Don't predict attack input!
|
||||
}
|
||||
currInputFrameDownsync = &InputFrameDownsync{
|
||||
InputFrameId: inputFrameId,
|
||||
InputList: currInputList,
|
||||
@@ -1092,18 +1098,12 @@ func (pR *Room) prefabInputFrameDownsync(inputFrameId int32) *InputFrameDownsync
|
||||
|
||||
func (pR *Room) markConfirmationIfApplicable() {
|
||||
inputFrameId1 := pR.LastAllConfirmedInputFrameId + 1
|
||||
gap := int32(4) // This value is hardcoded and doesn't need be much bigger, because the backend side is supposed to never lag when "false == BackendDynamicsEnabled".
|
||||
inputFrameId2 := inputFrameId1 + gap
|
||||
if inputFrameId2 > pR.InputsBuffer.EdFrameId {
|
||||
inputFrameId2 = pR.InputsBuffer.EdFrameId
|
||||
}
|
||||
|
||||
totPlayerCnt := uint32(pR.Capacity)
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
for inputFrameId := inputFrameId1; inputFrameId < inputFrameId2; inputFrameId++ {
|
||||
for inputFrameId := inputFrameId1; inputFrameId < pR.InputsBuffer.EdFrameId; inputFrameId++ {
|
||||
tmp := pR.InputsBuffer.GetByFrameId(inputFrameId)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("inputFrameId=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! InputsBuffer=%v", inputFrameId, pR.Id, pR.InputsBufferString(false)))
|
||||
panic(fmt.Sprintf("inputFrameId=%v doesn't exist for roomId=%v, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic (Or maybe you're having a 'Room.RenderCacheSize' too small)! InputsBuffer=%v", inputFrameId, pR.Id, pR.InputsBufferString(false)))
|
||||
}
|
||||
inputFrameDownsync := tmp.(*InputFrameDownsync)
|
||||
for _, player := range pR.Players {
|
||||
@@ -1114,7 +1114,7 @@ func (pR *Room) markConfirmationIfApplicable() {
|
||||
}
|
||||
inputFrameUpsync := tmp.(*InputFrameUpsync)
|
||||
indiceInJoinIndexBooleanArr := uint32(player.JoinIndex - 1)
|
||||
inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr] = pR.EncodeUpsyncCmd(inputFrameUpsync)
|
||||
inputFrameDownsync.InputList[indiceInJoinIndexBooleanArr] = inputFrameUpsync.Encoded
|
||||
inputFrameDownsync.ConfirmedList |= (1 << indiceInJoinIndexBooleanArr)
|
||||
}
|
||||
|
||||
@@ -1193,13 +1193,6 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
|
||||
}
|
||||
|
||||
nextRenderFrame := pR.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, pR.CollisionSysMap)
|
||||
// Update in the latest player pointers
|
||||
for playerId, playerDownsync := range nextRenderFrame.Players {
|
||||
pR.Players[playerId].VirtualGridX = playerDownsync.VirtualGridX
|
||||
pR.Players[playerId].VirtualGridY = playerDownsync.VirtualGridY
|
||||
pR.Players[playerId].Dir.Dx = playerDownsync.Dir.Dx
|
||||
pR.Players[playerId].Dir.Dy = playerDownsync.Dir.Dy
|
||||
}
|
||||
pR.RenderFrameBuffer.Put(nextRenderFrame)
|
||||
pR.CurDynamicsRenderFrameId++
|
||||
}
|
||||
@@ -1207,22 +1200,28 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
|
||||
|
||||
// TODO: Write unit-test for this function to compare with its frontend counter part
|
||||
func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame *InputFrameDownsync, currRenderFrame *RoomDownsyncFrame, collisionSysMap map[int32]*resolv.Object) *RoomDownsyncFrame {
|
||||
// TODO: Derive "nextRenderFramePlayers[*].CharacterState" as the frontend counter-part!
|
||||
nextRenderFramePlayers := make(map[int32]*PlayerDownsync, pR.Capacity)
|
||||
// Make a copy first
|
||||
for playerId, currPlayerDownsync := range currRenderFrame.Players {
|
||||
nextRenderFramePlayers[playerId] = &PlayerDownsync{
|
||||
Id: playerId,
|
||||
VirtualGridX: currPlayerDownsync.VirtualGridX,
|
||||
VirtualGridY: currPlayerDownsync.VirtualGridY,
|
||||
Dir: &Direction{
|
||||
Dx: currPlayerDownsync.Dir.Dx,
|
||||
Dy: currPlayerDownsync.Dir.Dy,
|
||||
},
|
||||
Speed: currPlayerDownsync.Speed,
|
||||
BattleState: currPlayerDownsync.BattleState,
|
||||
Score: currPlayerDownsync.Score,
|
||||
Removed: currPlayerDownsync.Removed,
|
||||
JoinIndex: currPlayerDownsync.JoinIndex,
|
||||
Id: playerId,
|
||||
VirtualGridX: currPlayerDownsync.VirtualGridX,
|
||||
VirtualGridY: currPlayerDownsync.VirtualGridY,
|
||||
DirX: currPlayerDownsync.DirX,
|
||||
DirY: currPlayerDownsync.DirY,
|
||||
CharacterState: currPlayerDownsync.CharacterState,
|
||||
Speed: currPlayerDownsync.Speed,
|
||||
BattleState: currPlayerDownsync.BattleState,
|
||||
Score: currPlayerDownsync.Score,
|
||||
Removed: currPlayerDownsync.Removed,
|
||||
JoinIndex: currPlayerDownsync.JoinIndex,
|
||||
FramesToRecover: currPlayerDownsync.FramesToRecover - 1,
|
||||
Hp: currPlayerDownsync.Hp,
|
||||
MaxHp: currPlayerDownsync.MaxHp,
|
||||
}
|
||||
if nextRenderFramePlayers[playerId].FramesToRecover < 0 {
|
||||
nextRenderFramePlayers[playerId].FramesToRecover = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,32 +1229,165 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
||||
Id: currRenderFrame.Id + 1,
|
||||
Players: nextRenderFramePlayers,
|
||||
CountdownNanos: (pR.BattleDurationNanos - int64(currRenderFrame.Id)*pR.RollbackEstimatedDtNanos),
|
||||
MeleeBullets: make([]*MeleeBullet, 0), // Is there any better way to reduce malloc/free impact, e.g. smart prediction for fixed memory allocation?
|
||||
}
|
||||
|
||||
bulletPushbacks := make([]Vec2D, pR.Capacity) // Guaranteed determinism regardless of traversal order
|
||||
effPushbacks := make([]Vec2D, pR.Capacity) // Guaranteed determinism regardless of traversal order
|
||||
|
||||
// Reset playerCollider position from the "virtual grid position"
|
||||
for playerId, player := range pR.Players {
|
||||
joinIndex := player.JoinIndex
|
||||
bulletPushbacks[joinIndex-1].X, bulletPushbacks[joinIndex-1].Y = float64(0), float64(0)
|
||||
effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
|
||||
currPlayerDownsync := currRenderFrame.Players[playerId]
|
||||
newVx, newVy := currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderAnchorPos(newVx, newVy, player.ColliderRadius, player.ColliderRadius, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
|
||||
}
|
||||
|
||||
// Check bullet-anything collisions first, because the pushbacks caused by bullets might later be reverted by player-barrier collision
|
||||
bulletColliders := make(map[int32]*resolv.Object, 0) // Will all be removed at the end of `applyInputFrameDownsyncDynamicsOnSingleRenderFrame` due to the need for being rollback-compatible
|
||||
removedBulletsAtCurrFrame := make(map[int32]int32, 0)
|
||||
for _, meleeBullet := range currRenderFrame.MeleeBullets {
|
||||
if (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames <= currRenderFrame.Id) && (meleeBullet.OriginatedRenderFrameId+meleeBullet.StartupFrames+meleeBullet.ActiveFrames > currRenderFrame.Id) {
|
||||
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
|
||||
collisionOffenderIndex := COLLISION_PLAYER_INDEX_PREFIX + meleeBullet.OffenderJoinIndex
|
||||
offenderCollider := collisionSysMap[collisionOffenderIndex]
|
||||
offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId]
|
||||
|
||||
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if 0 > offender.DirX {
|
||||
xfac = float64(-1.0)
|
||||
}
|
||||
offenderWx, offenderWy := VirtualGridToWorldPos(offender.VirtualGridX, offender.VirtualGridY, pR.VirtualGridToWorldRatio)
|
||||
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||
|
||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "MeleeBullet")
|
||||
newBulletCollider.Data = meleeBullet
|
||||
pR.Space.Add(newBulletCollider)
|
||||
collisionSysMap[collisionBulletIndex] = newBulletCollider
|
||||
bulletColliders[collisionBulletIndex] = newBulletCollider
|
||||
|
||||
Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet is added to collisionSys at currRenderFrame.id=%v as start-up frames ended and active frame is not yet ended: %v, from offenderCollider=%v, xfac=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(newBulletCollider.Shape.(*resolv.ConvexPolygon)), ConvexPolygonStr(offenderCollider.Shape.(*resolv.ConvexPolygon)), xfac))
|
||||
}
|
||||
}
|
||||
|
||||
for _, bulletCollider := range bulletColliders {
|
||||
shouldRemove := false
|
||||
meleeBullet := bulletCollider.Data.(*MeleeBullet)
|
||||
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
|
||||
bulletShape := bulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||
if collision := bulletCollider.Check(0, 0); collision != nil {
|
||||
offender := currRenderFrame.Players[meleeBullet.OffenderPlayerId]
|
||||
for _, obj := range collision.Objects {
|
||||
defenderShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||
switch t := obj.Data.(type) {
|
||||
case *Player:
|
||||
if meleeBullet.OffenderPlayerId != t.Id {
|
||||
if overlapped, _, _, _ := CalcPushbacks(0, 0, bulletShape, defenderShape); overlapped {
|
||||
xfac := float64(1.0) // By now, straight Punch offset doesn't respect "y-axis"
|
||||
if 0 > offender.DirX {
|
||||
xfac = float64(-1.0)
|
||||
}
|
||||
bulletPushbacks[t.JoinIndex-1].X += xfac * meleeBullet.Pushback
|
||||
nextRenderFramePlayers[t.Id].CharacterState = ATK_CHARACTER_STATE_ATKED1
|
||||
oldFramesToRecover := nextRenderFramePlayers[t.Id].FramesToRecover
|
||||
if meleeBullet.HitStunFrames > oldFramesToRecover {
|
||||
nextRenderFramePlayers[t.Id].FramesToRecover = meleeBullet.HitStunFrames
|
||||
}
|
||||
Logger.Debug(fmt.Sprintf("roomId=%v, a meleeBullet collides w/ player at currRenderFrame.id=%v: b=%v, p=%v", pR.Id, currRenderFrame.Id, ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape)))
|
||||
}
|
||||
}
|
||||
default:
|
||||
Logger.Debug(fmt.Sprintf("Bullet %v collided with non-player %v: roomId=%v, currRenderFrame.Id=%v, delayedInputFrame.Id=%v, objDataType=%t, objData=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(defenderShape), pR.Id, currRenderFrame.Id, delayedInputFrame.InputFrameId, obj.Data, obj.Data))
|
||||
}
|
||||
}
|
||||
shouldRemove = true
|
||||
}
|
||||
if shouldRemove {
|
||||
removedBulletsAtCurrFrame[collisionBulletIndex] = 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, meleeBullet := range currRenderFrame.MeleeBullets {
|
||||
collisionBulletIndex := COLLISION_BULLET_INDEX_PREFIX + meleeBullet.BattleLocalId
|
||||
if bulletCollider, existent := collisionSysMap[collisionBulletIndex]; existent {
|
||||
bulletCollider.Space.Remove(bulletCollider)
|
||||
delete(collisionSysMap, collisionBulletIndex)
|
||||
}
|
||||
if _, existent := removedBulletsAtCurrFrame[collisionBulletIndex]; existent {
|
||||
continue
|
||||
}
|
||||
toRet.MeleeBullets = append(toRet.MeleeBullets, meleeBullet)
|
||||
}
|
||||
|
||||
if nil != delayedInputFrame {
|
||||
var delayedInputFrameForPrevRenderFrame *InputFrameDownsync = nil
|
||||
tmp := pR.InputsBuffer.GetByFrameId(pR.ConvertToInputFrameId(currRenderFrame.Id-1, pR.InputDelayFrames))
|
||||
if nil != tmp {
|
||||
delayedInputFrameForPrevRenderFrame = tmp.(*InputFrameDownsync)
|
||||
}
|
||||
inputList := delayedInputFrame.InputList
|
||||
effPushbacks := make([]Vec2D, pR.Capacity) // Guaranteed determinism regardless of traversal order
|
||||
// Process player inputs
|
||||
for playerId, player := range pR.Players {
|
||||
joinIndex := player.JoinIndex
|
||||
effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
|
||||
currPlayerDownsync := currRenderFrame.Players[playerId]
|
||||
encodedInput := inputList[joinIndex-1]
|
||||
decodedInput := DIRECTION_DECODER[encodedInput]
|
||||
proposedVirtualGridDx, proposedVirtualGridDy := (decodedInput[0] + decodedInput[0]*currPlayerDownsync.Speed), (decodedInput[1] + decodedInput[1]*currPlayerDownsync.Speed)
|
||||
newVx, newVy := (currPlayerDownsync.VirtualGridX + proposedVirtualGridDx), (currPlayerDownsync.VirtualGridY + proposedVirtualGridDy)
|
||||
// Reset playerCollider position from the "virtual grid position"
|
||||
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
|
||||
playerCollider := collisionSysMap[collisionPlayerIndex]
|
||||
playerCollider.X, playerCollider.Y = VirtualGridToPolygonColliderAnchorPos(newVx, newVy, player.ColliderRadius, player.ColliderRadius, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.VirtualGridToWorldRatio)
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[playerId]
|
||||
if 0 < thatPlayerInNextFrame.FramesToRecover {
|
||||
// No need to process inputs for this player, but there might be bullet pushbacks on this player
|
||||
playerCollider.X += bulletPushbacks[joinIndex-1].X
|
||||
playerCollider.Y += bulletPushbacks[joinIndex-1].Y
|
||||
// Update in the collision system
|
||||
playerCollider.Update()
|
||||
if 0 != bulletPushbacks[joinIndex-1].X || 0 != bulletPushbacks[joinIndex-1].Y {
|
||||
Logger.Info(fmt.Sprintf("roomId=%v, playerId=%v is pushed back by (%.2f, %.2f) by bullet impacts, now its framesToRecover is %d at currRenderFrame.id=%v", pR.Id, playerId, bulletPushbacks[joinIndex-1].X, bulletPushbacks[joinIndex-1].Y, thatPlayerInNextFrame.FramesToRecover, currRenderFrame.Id))
|
||||
}
|
||||
continue
|
||||
}
|
||||
currPlayerDownsync := currRenderFrame.Players[playerId]
|
||||
decodedInput := pR.decodeInput(inputList[joinIndex-1])
|
||||
prevBtnALevel := int32(0)
|
||||
if nil != delayedInputFrameForPrevRenderFrame {
|
||||
prevDecodedInput := pR.decodeInput(delayedInputFrameForPrevRenderFrame.InputList[joinIndex-1])
|
||||
prevBtnALevel = prevDecodedInput.BtnALevel
|
||||
}
|
||||
|
||||
if decodedInput.BtnALevel > prevBtnALevel {
|
||||
punchSkillId := int32(1)
|
||||
punchConfig := pR.MeleeSkillConfig[punchSkillId]
|
||||
var newMeleeBullet MeleeBullet = *punchConfig
|
||||
newMeleeBullet.BattleLocalId = pR.BulletBattleLocalIdCounter
|
||||
pR.BulletBattleLocalIdCounter += 1
|
||||
newMeleeBullet.OffenderJoinIndex = joinIndex
|
||||
newMeleeBullet.OffenderPlayerId = playerId
|
||||
newMeleeBullet.OriginatedRenderFrameId = currRenderFrame.Id
|
||||
toRet.MeleeBullets = append(toRet.MeleeBullets, &newMeleeBullet)
|
||||
thatPlayerInNextFrame.FramesToRecover = newMeleeBullet.RecoveryFrames
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_ATK1
|
||||
Logger.Info(fmt.Sprintf("roomId=%v, playerId=%v triggered a rising-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId))
|
||||
|
||||
} else if decodedInput.BtnALevel < prevBtnALevel {
|
||||
Logger.Debug(fmt.Sprintf("roomId=%v, playerId=%v triggered a falling-edge of btnA at currRenderFrame.id=%v, delayedInputFrame.id=%v", pR.Id, playerId, currRenderFrame.Id, delayedInputFrame.InputFrameId))
|
||||
} else {
|
||||
// No bullet trigger, process movement inputs
|
||||
if 0 != decodedInput.Dx || 0 != decodedInput.Dy {
|
||||
thatPlayerInNextFrame.DirX = decodedInput.Dx
|
||||
thatPlayerInNextFrame.DirY = decodedInput.Dy
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_WALKING
|
||||
} else {
|
||||
thatPlayerInNextFrame.CharacterState = ATK_CHARACTER_STATE_IDLE1
|
||||
}
|
||||
}
|
||||
|
||||
movementX, movementY := VirtualGridToWorldPos(decodedInput.Dx+decodedInput.Dx*currPlayerDownsync.Speed, decodedInput.Dy+decodedInput.Dy*currPlayerDownsync.Speed, pR.VirtualGridToWorldRatio)
|
||||
playerCollider.X += movementX
|
||||
playerCollider.Y += movementY
|
||||
|
||||
// Update in the collision system
|
||||
playerCollider.Update()
|
||||
|
||||
if 0 < encodedInput {
|
||||
Logger.Debug(fmt.Sprintf("Checking collision for playerId=%v: virtual (%d, %d) -> (%d, %d), now playerShape=%v", playerId, currPlayerDownsync.VirtualGridX, currPlayerDownsync.VirtualGridY, newVx, newVy, ConvexPolygonStr(playerCollider.Shape.(*resolv.ConvexPolygon))))
|
||||
nextRenderFramePlayers[playerId].Dir.Dx = decodedInput[0]
|
||||
nextRenderFramePlayers[playerId].Dir.Dy = decodedInput[1]
|
||||
}
|
||||
}
|
||||
|
||||
// handle pushbacks upon collision after all movements treated as simultaneous
|
||||
@@ -1285,16 +1417,26 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
|
||||
|
||||
// Update "virtual grid position"
|
||||
newVx, newVy := PolygonColliderAnchorToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, player.ColliderRadius, player.ColliderRadius, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, pR.WorldToVirtualGridRatio)
|
||||
nextRenderFramePlayers[playerId].VirtualGridX = newVx
|
||||
nextRenderFramePlayers[playerId].VirtualGridY = newVy
|
||||
thatPlayerInNextFrame := nextRenderFramePlayers[playerId]
|
||||
thatPlayerInNextFrame.VirtualGridX, thatPlayerInNextFrame.VirtualGridY = newVx, newVy
|
||||
}
|
||||
|
||||
Logger.Debug(fmt.Sprintf("After applyInputFrameDownsyncDynamicsOnSingleRenderFrame: currRenderFrame.Id=%v, inputList=%v, currRenderFrame.Players=%v, nextRenderFramePlayers=%v, toRet.Players=%v", currRenderFrame.Id, inputList, currRenderFrame.Players, nextRenderFramePlayers, toRet.Players))
|
||||
Logger.Debug(fmt.Sprintf("After applyInputFrameDownsyncDynamicsOnSingleRenderFrame: currRenderFrame.Id=%v, inputList=%v, currRenderFrame.Players=%v, nextRenderFramePlayers=%v", currRenderFrame.Id, inputList, currRenderFrame.Players, nextRenderFramePlayers))
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
||||
func (pR *Room) decodeInput(encodedInput uint64) *InputFrameDecoded {
|
||||
encodedDirection := (encodedInput & uint64(15))
|
||||
btnALevel := int32((encodedInput >> 4) & 1)
|
||||
return &InputFrameDecoded{
|
||||
Dx: DIRECTION_DECODER[encodedDirection][0],
|
||||
Dy: DIRECTION_DECODER[encodedDirection][1],
|
||||
BtnALevel: btnALevel,
|
||||
}
|
||||
}
|
||||
|
||||
func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
|
||||
return 0 == (inputFrameId % 10)
|
||||
}
|
||||
@@ -1302,12 +1444,13 @@ func (pR *Room) inputFrameIdDebuggable(inputFrameId int32) bool {
|
||||
func (pR *Room) refreshColliders(spaceW, spaceH int32) {
|
||||
// Kindly note that by now, we've already got all the shapes in the tmx file into "pR.(Players | Barriers)" from "ParseTmxLayersAndGroups"
|
||||
|
||||
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 2) // the approx minimum distance a player can move per frame in world coordinate
|
||||
space := resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
||||
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 1) // the approx minimum distance a player can move per frame in world coordinate
|
||||
pR.Space = resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
||||
for _, player := range pR.Players {
|
||||
wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
|
||||
playerCollider := GenerateRectCollider(wx, wy, player.ColliderRadius*2, player.ColliderRadius*2, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Player")
|
||||
space.Add(playerCollider)
|
||||
playerCollider.Data = player
|
||||
pR.Space.Add(playerCollider)
|
||||
// Keep track of the collider in "pR.CollisionSysMap"
|
||||
joinIndex := player.JoinIndex
|
||||
pR.PlayersArr[joinIndex-1] = player
|
||||
@@ -1318,7 +1461,7 @@ func (pR *Room) refreshColliders(spaceW, spaceH int32) {
|
||||
for _, barrier := range pR.Barriers {
|
||||
boundaryUnaligned := barrier.Boundary
|
||||
barrierCollider := GenerateConvexPolygonCollider(boundaryUnaligned, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY, "Barrier")
|
||||
space.Add(barrierCollider)
|
||||
pR.Space.Add(barrierCollider)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -247,8 +247,8 @@ func Serve(c *gin.Context) {
|
||||
bciFrame := &pb.BattleColliderInfo{
|
||||
BoundRoomId: pRoom.Id,
|
||||
StageName: pRoom.StageName,
|
||||
StrToVec2DListMap: pRoom.RawBattleStrToVec2DListMap,
|
||||
StrToPolygon2DListMap: pRoom.RawBattleStrToPolygon2DListMap,
|
||||
StrToVec2DListMap: pRoom.StrToVec2DListMap,
|
||||
StrToPolygon2DListMap: pRoom.StrToPolygon2DListMap,
|
||||
StageDiscreteW: pRoom.StageDiscreteW,
|
||||
StageDiscreteH: pRoom.StageDiscreteH,
|
||||
StageTileW: pRoom.StageTileW,
|
||||
@@ -269,6 +269,10 @@ func Serve(c *gin.Context) {
|
||||
|
||||
WorldToVirtualGridRatio: pRoom.WorldToVirtualGridRatio,
|
||||
VirtualGridToWorldRatio: pRoom.VirtualGridToWorldRatio,
|
||||
|
||||
SpAtkLookupFrames: pRoom.SpAtkLookupFrames,
|
||||
RenderCacheSize: pRoom.RenderCacheSize,
|
||||
MeleeSkillConfig: pRoom.MeleeSkillConfig,
|
||||
}
|
||||
|
||||
resp := &pb.WsResp{
|
||||
|
BIN
charts/AttackTriggerCases.jpg
Normal file
After Width: | Height: | Size: 176 KiB |
@@ -1,3 +1,8 @@
|
||||
# Double playback speed of a video
|
||||
```
|
||||
ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
|
||||
```
|
||||
|
||||
# GIF creation cmd reference
|
||||
```
|
||||
ffmpeg -ss 12 -t 13 -i input.mp4 -vf "fps=10,scale=480:-1" -loop 0 output.gif
|
||||
|
Before Width: | Height: | Size: 2.7 MiB |
BIN
charts/melee_attack_2.gif
Normal file
After Width: | Height: | Size: 5.3 MiB |
@@ -4,6 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
dnmshared v0.0.0
|
||||
battle_srv v0.0.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/hajimehoshi/ebiten/v2 v2.4.7
|
||||
github.com/solarlune/resolv v0.5.1
|
||||
@@ -26,3 +27,4 @@ require (
|
||||
)
|
||||
|
||||
replace dnmshared => ../dnmshared
|
||||
replace battle_srv => ../battle_srv
|
||||
|
@@ -85,8 +85,9 @@ type Game struct {
|
||||
|
||||
func NewGame() *Game {
|
||||
|
||||
stageName := "simple" // Use this for calibration
|
||||
// stageName := "simple" // Use this for calibration in isometric orientation
|
||||
// stageName := "richsoil"
|
||||
stageName := "dungeon"
|
||||
stageDiscreteW, stageDiscreteH, stageTileW, stageTileH, playerPosMap, barrierMap, err := parseStage(stageName)
|
||||
if nil != err {
|
||||
panic(err)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "battle_srv/protos"
|
||||
. "dnmshared"
|
||||
. "dnmshared/sharedprotos"
|
||||
"fmt"
|
||||
@@ -56,7 +57,7 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
||||
|
||||
world.Space = space
|
||||
|
||||
moveToCollide := true
|
||||
moveToCollide := false
|
||||
if moveToCollide {
|
||||
newVx, newVy := int32(-2959), int32(-2261)
|
||||
effPushback := Vec2D{X: float64(0), Y: float64(0)}
|
||||
@@ -84,6 +85,75 @@ func NewWorldColliderDisplay(game *Game, stageDiscreteW, stageDiscreteH, stageTi
|
||||
Logger.Info(fmt.Sprintf("effPushback={%v, %v}", effPushback.X, effPushback.Y))
|
||||
}
|
||||
}
|
||||
meleeBullet := &MeleeBullet{
|
||||
// for offender
|
||||
StartupFrames: int32(18),
|
||||
ActiveFrames: int32(1),
|
||||
RecoveryFrames: int32(61),
|
||||
RecoveryFramesOnBlock: int32(61),
|
||||
RecoveryFramesOnHit: int32(61),
|
||||
Moveforward: &Vec2D{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
},
|
||||
HitboxOffset: float64(24.0),
|
||||
HitboxSize: &Vec2D{
|
||||
X: float64(45.0),
|
||||
Y: float64(32.0),
|
||||
},
|
||||
|
||||
// for defender
|
||||
HitStunFrames: int32(18),
|
||||
BlockStunFrames: int32(9),
|
||||
Pushback: float64(22.0),
|
||||
ReleaseTriggerType: int32(1), // 1: rising-edge, 2: falling-edge
|
||||
Damage: int32(5),
|
||||
}
|
||||
bulletLeftToRight := true
|
||||
if bulletLeftToRight {
|
||||
xfac := float64(1.0)
|
||||
offenderWx, offenderWy := playerPosList.Eles[0].X, playerPosList.Eles[0].Y
|
||||
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||
|
||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet")
|
||||
space.Add(newBulletCollider)
|
||||
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||
Logger.Warn(fmt.Sprintf("bullet ->: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
|
||||
|
||||
if collision := newBulletCollider.Check(0, 0); collision != nil {
|
||||
for _, obj := range collision.Objects {
|
||||
objShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped {
|
||||
Logger.Warn(fmt.Sprintf("bullet ->: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY))
|
||||
} else {
|
||||
Logger.Warn(fmt.Sprintf("bullet ->: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bulletRightToLeft := true
|
||||
if bulletRightToLeft {
|
||||
xfac := float64(-1.0)
|
||||
offenderWx, offenderWy := playerPosList.Eles[1].X, playerPosList.Eles[1].Y
|
||||
bulletWx, bulletWy := offenderWx+xfac*meleeBullet.HitboxOffset, offenderWy
|
||||
|
||||
newBulletCollider := GenerateRectCollider(bulletWx, bulletWy, meleeBullet.HitboxSize.X, meleeBullet.HitboxSize.Y, spaceOffsetX, spaceOffsetY, "MeleeBullet")
|
||||
space.Add(newBulletCollider)
|
||||
bulletShape := newBulletCollider.Shape.(*resolv.ConvexPolygon)
|
||||
Logger.Warn(fmt.Sprintf("bullet <-: Added bullet collider to space: a=%v", ConvexPolygonStr(bulletShape)))
|
||||
|
||||
if collision := newBulletCollider.Check(0, 0); collision != nil {
|
||||
for _, obj := range collision.Objects {
|
||||
objShape := obj.Shape.(*resolv.ConvexPolygon)
|
||||
if overlapped, pushbackX, pushbackY, overlapResult := CalcPushbacks(0, 0, bulletShape, objShape); overlapped {
|
||||
Logger.Warn(fmt.Sprintf("bullet <-: Overlapped: a=%v, b=%v, pushbackX=%v, pushbackY=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), pushbackX, pushbackY))
|
||||
} else {
|
||||
Logger.Warn(fmt.Sprintf("bullet <-: Collided BUT not overlapped: a=%v, b=%v, overlapResult=%v", ConvexPolygonStr(bulletShape), ConvexPolygonStr(objShape), overlapResult))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return world
|
||||
}
|
||||
@@ -98,6 +168,9 @@ func (world *WorldColliderDisplay) Draw(screen *ebiten.Image) {
|
||||
if o.HasTags("Player") {
|
||||
drawColor := color.RGBA{0, 255, 0, 255}
|
||||
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||
} else if o.HasTags("MeleeBullet") {
|
||||
drawColor := color.RGBA{0, 0, 255, 255}
|
||||
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||
} else {
|
||||
drawColor := color.RGBA{60, 60, 60, 255}
|
||||
DrawPolygon(screen, o.Shape.(*resolv.ConvexPolygon), drawColor)
|
||||
|
@@ -20,6 +20,10 @@ func ConvexPolygonStr(body *resolv.ConvexPolygon) string {
|
||||
|
||||
func GenerateRectCollider(origX, origY, w, h, spaceOffsetX, spaceOffsetY float64, tag string) *resolv.Object {
|
||||
cx, cy := WorldToPolygonColliderAnchorPos(origX, origY, w*0.5, h*0.5, spaceOffsetX, spaceOffsetY)
|
||||
return GenerateRectColliderInCollisionSpace(cx, cy, w, h, tag)
|
||||
}
|
||||
|
||||
func GenerateRectColliderInCollisionSpace(cx, cy, w, h float64, tag string) *resolv.Object {
|
||||
collider := resolv.NewObject(cx, cy, w, h, tag)
|
||||
shape := resolv.NewRectangle(0, 0, w, h)
|
||||
collider.SetShape(shape)
|
||||
|
@@ -406,6 +406,12 @@ type TileRectilinearSize struct {
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerVec *Vec2D) Vec2D {
|
||||
if "orthogonal" == pTmxMapIns.Orientation {
|
||||
return Vec2D{
|
||||
X: continuousObjLayerVec.X,
|
||||
Y: -continuousObjLayerVec.Y,
|
||||
}
|
||||
}
|
||||
var tileRectilinearSize TileRectilinearSize
|
||||
tileRectilinearSize.Width = float64(pTmxMapIns.TileWidth)
|
||||
tileRectilinearSize.Height = float64(pTmxMapIns.TileHeight)
|
||||
@@ -428,18 +434,24 @@ func (pTmxMapIns *TmxMap) continuousObjLayerVecToContinuousMapNodeVec(continuous
|
||||
}
|
||||
|
||||
func (pTmxMapIns *TmxMap) continuousObjLayerOffsetToContinuousMapNodePos(continuousObjLayerOffset *Vec2D) Vec2D {
|
||||
layerOffset := Vec2D{
|
||||
X: 0,
|
||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||
var layerOffset Vec2D
|
||||
if "orthogonal" == pTmxMapIns.Orientation {
|
||||
layerOffset = Vec2D{
|
||||
X: -float64(pTmxMapIns.Width*pTmxMapIns.TileWidth) * 0.5,
|
||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||
}
|
||||
} else {
|
||||
// "isometric" == pTmxMapIns.Orientation
|
||||
layerOffset = Vec2D{
|
||||
X: 0,
|
||||
Y: float64(pTmxMapIns.Height*pTmxMapIns.TileHeight) * 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
calibratedVec := continuousObjLayerOffset
|
||||
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(calibratedVec)
|
||||
convertedVec := pTmxMapIns.continuousObjLayerVecToContinuousMapNodeVec(continuousObjLayerOffset)
|
||||
|
||||
toRet := Vec2D{
|
||||
return Vec2D{
|
||||
X: layerOffset.X + convertedVec.X,
|
||||
Y: layerOffset.Y + convertedVec.Y,
|
||||
}
|
||||
|
||||
return toRet
|
||||
}
|
||||
|
BIN
dragonBonesAnimProjects/SoldierElf.dbproj
Normal file
BIN
dragonBonesAnimProjects/SoldierFireGhost.dbproj
Normal file
BIN
dragonBonesAnimProjects/SoldierWaterGhost.dbproj
Normal file
1
dragonBonesAnimProjects/library/SoldierElf.json
Normal file
@@ -0,0 +1 @@
|
||||
{"width":128,"imagePath":"SoldierElf_tex.png","height":128,"name":"SoldierElf","SubTexture":[{"frameHeight":45,"y":1,"frameX":0,"width":34,"frameY":0,"height":44,"name":"cape","frameWidth":34,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":101,"height":11,"name":"thigh_l","x":86},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":88,"height":11,"name":"thigh_r","x":77},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameHeight":39,"y":47,"frameX":-2,"width":34,"frameY":0,"height":39,"name":"we_bl_4_f_1","frameWidth":36,"x":70}]}
|
BIN
dragonBonesAnimProjects/library/SoldierElf.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
1
dragonBonesAnimProjects/library/SoldierFireGhost.json
Normal file
@@ -0,0 +1 @@
|
||||
{"imagePath":"Soldier_02_tex.png","width":128,"name":"Soldier_02","SubTexture":[{"x":53,"y":44,"width":23,"name":"biu","height":22},{"x":76,"y":68,"width":9,"name":"rightArm","height":14},{"y":35,"frameY":0,"height":32,"frameWidth":29,"frameX":-1,"frameHeight":32,"width":27,"name":"yinmoqe00","x":89},{"x":53,"y":1,"width":34,"name":"body","height":41},{"x":78,"y":44,"width":9,"name":"rightShoulder","height":13},{"y":50,"frameY":0,"height":18,"frameWidth":19,"frameX":0,"frameHeight":18,"width":19,"name":"rightFrontArm","x":23},{"x":23,"y":70,"width":14,"name":"rightHand","height":14},{"y":68,"frameY":0,"height":12,"frameWidth":12,"frameX":0,"frameHeight":12,"width":12,"name":"leftArm","x":62},{"x":1,"y":73,"width":13,"name":"leftShoulder","height":12},{"x":1,"y":50,"width":20,"name":"leftFrontArm","height":21},{"x":89,"y":1,"width":33,"name":"head","height":32},{"x":1,"y":1,"width":50,"name":"head2","height":47},{"x":44,"y":68,"width":16,"name":"leftHand","height":14},{"y":59,"frameY":-2,"height":4,"frameWidth":8,"frameX":-1,"frameHeight":8,"width":4,"name":"huomiao01","x":78}],"height":128}
|
BIN
dragonBonesAnimProjects/library/SoldierFireGhost.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
1
dragonBonesAnimProjects/library/SoldierWaterGhost.json
Normal file
@@ -0,0 +1 @@
|
||||
{"SubTexture":[{"width":23,"y":44,"height":22,"name":"biu","x":53},{"width":9,"y":68,"height":14,"name":"rightArm","x":76},{"y":35,"frameX":-1,"frameY":0,"width":27,"frameWidth":29,"height":32,"name":"yinmoqe00","frameHeight":32,"x":89},{"width":34,"y":1,"height":41,"name":"body","x":53},{"width":9,"y":44,"height":13,"name":"rightShoulder","x":78},{"y":50,"frameX":0,"frameY":0,"width":19,"frameWidth":19,"height":18,"name":"rightFrontArm","frameHeight":18,"x":23},{"width":14,"y":70,"height":14,"name":"rightHand","x":23},{"y":68,"frameX":0,"frameY":0,"width":12,"frameWidth":12,"height":12,"name":"leftArm","frameHeight":12,"x":62},{"width":13,"y":73,"height":12,"name":"leftShoulder","x":1},{"width":20,"y":50,"height":21,"name":"leftFrontArm","x":1},{"width":33,"y":1,"height":32,"name":"head","x":89},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"width":16,"y":68,"height":14,"name":"leftHand","x":44},{"y":59,"frameX":-1,"frameY":-2,"width":4,"frameWidth":8,"height":4,"name":"huomiao01","frameHeight":8,"x":78}],"width":128,"height":128,"name":"SoldierWaterGhost","imagePath":"SoldierWaterGhost_tex.png"}
|
BIN
dragonBonesAnimProjects/library/SoldierWaterGhost.png
Normal file
After Width: | Height: | Size: 11 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "51c54820-d753-4be8-a855-5760eed8f7ef",
|
||||
"uuid": "8f2f76c7-649c-414a-80be-b2daef4ed580",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
@@ -0,0 +1 @@
|
||||
{"SubTexture":[{"y":50,"frameX":-2,"frameY":-2,"width":19,"frameWidth":23,"height":19,"name":"biu","frameHeight":22,"x":1},{"width":9,"y":50,"height":14,"name":"rightArm","x":42},{"y":34,"frameX":-6,"frameY":0,"width":20,"frameWidth":29,"height":32,"name":"yinmoqe00","frameHeight":32,"x":88},{"y":1,"frameX":0,"frameY":0,"width":33,"frameWidth":34,"height":39,"name":"body","frameHeight":41,"x":53},{"width":9,"y":56,"height":13,"name":"rightShoulder","x":74},{"y":50,"frameX":0,"frameY":0,"width":18,"frameWidth":19,"height":17,"name":"rightFrontArm","frameHeight":18,"x":22},{"width":14,"y":50,"height":14,"name":"rightHand","x":110},{"width":12,"y":42,"height":12,"name":"leftArm","x":74},{"width":13,"y":66,"height":12,"name":"leftShoulder","x":110},{"y":42,"frameX":-1,"frameY":0,"width":19,"frameWidth":20,"height":21,"name":"leftFrontArm","frameHeight":21,"x":53},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"y":1,"frameX":-1,"frameY":0,"width":32,"frameWidth":33,"height":31,"name":"head","frameHeight":32,"x":88},{"width":16,"y":34,"height":14,"name":"leftHand","x":110},{"y":1,"frameX":-2,"frameY":-3,"width":2,"frameWidth":8,"height":2,"name":"huomiao01","frameHeight":8,"x":122}],"width":128,"height":128,"name":"SoldierWaterGhost","imagePath":"SoldierWaterGhost_tex.png"}
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"uuid": "e9e703e9-3589-4713-b889-28b23406d220",
|
||||
"atlasJson": "{\"SubTexture\":[{\"y\":50,\"frameX\":-2,\"frameY\":-2,\"width\":19,\"frameWidth\":23,\"height\":19,\"name\":\"biu\",\"frameHeight\":22,\"x\":1},{\"width\":9,\"y\":50,\"height\":14,\"name\":\"rightArm\",\"x\":42},{\"y\":34,\"frameX\":-6,\"frameY\":0,\"width\":20,\"frameWidth\":29,\"height\":32,\"name\":\"yinmoqe00\",\"frameHeight\":32,\"x\":88},{\"y\":1,\"frameX\":0,\"frameY\":0,\"width\":33,\"frameWidth\":34,\"height\":39,\"name\":\"body\",\"frameHeight\":41,\"x\":53},{\"width\":9,\"y\":56,\"height\":13,\"name\":\"rightShoulder\",\"x\":74},{\"y\":50,\"frameX\":0,\"frameY\":0,\"width\":18,\"frameWidth\":19,\"height\":17,\"name\":\"rightFrontArm\",\"frameHeight\":18,\"x\":22},{\"width\":14,\"y\":50,\"height\":14,\"name\":\"rightHand\",\"x\":110},{\"width\":12,\"y\":42,\"height\":12,\"name\":\"leftArm\",\"x\":74},{\"width\":13,\"y\":66,\"height\":12,\"name\":\"leftShoulder\",\"x\":110},{\"y\":42,\"frameX\":-1,\"frameY\":0,\"width\":19,\"frameWidth\":20,\"height\":21,\"name\":\"leftFrontArm\",\"frameHeight\":21,\"x\":53},{\"width\":50,\"y\":1,\"height\":47,\"name\":\"head2\",\"x\":1},{\"y\":1,\"frameX\":-1,\"frameY\":0,\"width\":32,\"frameWidth\":33,\"height\":31,\"name\":\"head\",\"frameHeight\":32,\"x\":88},{\"width\":16,\"y\":34,\"height\":14,\"name\":\"leftHand\",\"x\":110},{\"y\":1,\"frameX\":-2,\"frameY\":-3,\"width\":2,\"frameWidth\":8,\"height\":2,\"name\":\"huomiao01\",\"frameHeight\":8,\"x\":122}],\"width\":128,\"height\":128,\"name\":\"SoldierWaterGhost\",\"imagePath\":\"SoldierWaterGhost_tex.png\"}",
|
||||
"texture": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||
"subMetas": {}
|
||||
}
|
After Width: | Height: | Size: 6.8 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"SoldierWaterGhost_tex": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "52fb0606-bbea-433c-803b-bf5ce936a0df",
|
||||
"rawTextureUuid": "def168c3-3f07-43f9-a460-36b397c70a57",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -0.5,
|
||||
"offsetY": 24.5,
|
||||
"trimX": 1,
|
||||
"trimY": 1,
|
||||
"width": 125,
|
||||
"height": 77,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 128,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
7
frontend/assets/resources/animation/SoldierElf.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "2202f4f4-b792-4dea-8302-633315aded66",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
{"width":128,"SubTexture":[{"frameWidth":34,"y":1,"frameHeight":45,"width":34,"frameX":0,"height":44,"name":"cape","frameY":0,"x":70},{"width":10,"y":107,"height":14,"name":"shouder_l","x":74},{"width":11,"y":107,"height":14,"name":"forearm_l","x":61},{"width":15,"y":93,"height":16,"name":"hand_l","x":1},{"width":30,"y":61,"height":30,"name":"weapon_hand_l","x":1},{"width":8,"y":88,"height":11,"name":"thigh_l","x":77},{"width":12,"y":93,"height":17,"name":"calf_l","x":18},{"width":20,"y":113,"height":8,"name":"foot_l","x":39},{"width":28,"y":61,"height":31,"name":"pelvis","x":33},{"width":8,"y":101,"height":11,"name":"thigh_r","x":86},{"width":12,"y":88,"height":17,"name":"calf_r","x":63},{"width":20,"y":113,"height":8,"name":"foot_r","x":17},{"width":13,"y":94,"height":12,"name":"shouder_r","x":45},{"width":67,"y":1,"height":58,"name":"chest","x":1},{"width":11,"y":94,"height":17,"name":"forearm_r","x":32},{"width":14,"y":111,"height":13,"name":"hand_r","x":1},{"frameWidth":36,"y":47,"frameHeight":39,"width":34,"frameX":-2,"height":39,"name":"we_bl_4_f_1","frameY":0,"x":70}],"height":128,"name":"SoldierElf","imagePath":"SoldierElf_tex.png"}
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"uuid": "24d7bb8f-577c-4e5d-b730-56613ca8685d",
|
||||
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":34,\"y\":1,\"frameHeight\":45,\"width\":34,\"frameX\":0,\"height\":44,\"name\":\"cape\",\"frameY\":0,\"x\":70},{\"width\":10,\"y\":107,\"height\":14,\"name\":\"shouder_l\",\"x\":74},{\"width\":11,\"y\":107,\"height\":14,\"name\":\"forearm_l\",\"x\":61},{\"width\":15,\"y\":93,\"height\":16,\"name\":\"hand_l\",\"x\":1},{\"width\":30,\"y\":61,\"height\":30,\"name\":\"weapon_hand_l\",\"x\":1},{\"width\":8,\"y\":88,\"height\":11,\"name\":\"thigh_l\",\"x\":77},{\"width\":12,\"y\":93,\"height\":17,\"name\":\"calf_l\",\"x\":18},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_l\",\"x\":39},{\"width\":28,\"y\":61,\"height\":31,\"name\":\"pelvis\",\"x\":33},{\"width\":8,\"y\":101,\"height\":11,\"name\":\"thigh_r\",\"x\":86},{\"width\":12,\"y\":88,\"height\":17,\"name\":\"calf_r\",\"x\":63},{\"width\":20,\"y\":113,\"height\":8,\"name\":\"foot_r\",\"x\":17},{\"width\":13,\"y\":94,\"height\":12,\"name\":\"shouder_r\",\"x\":45},{\"width\":67,\"y\":1,\"height\":58,\"name\":\"chest\",\"x\":1},{\"width\":11,\"y\":94,\"height\":17,\"name\":\"forearm_r\",\"x\":32},{\"width\":14,\"y\":111,\"height\":13,\"name\":\"hand_r\",\"x\":1},{\"frameWidth\":36,\"y\":47,\"frameHeight\":39,\"width\":34,\"frameX\":-2,\"height\":39,\"name\":\"we_bl_4_f_1\",\"frameY\":0,\"x\":70}],\"height\":128,\"name\":\"SoldierElf\",\"imagePath\":\"SoldierElf_tex.png\"}",
|
||||
"texture": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||
"subMetas": {}
|
||||
}
|
After Width: | Height: | Size: 7.1 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "c30bd4d7-efdc-410c-8bdf-4a3dfc77bebd",
|
||||
"uuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
@@ -9,21 +9,21 @@
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"Tile_W300_H300_S01": {
|
||||
"SoldierElf_tex": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "66b49304-7b5b-442c-92a5-d2b368abf659",
|
||||
"rawTextureUuid": "c30bd4d7-efdc-410c-8bdf-4a3dfc77bebd",
|
||||
"uuid": "c62e1779-f92b-40d3-bf4f-7ab747e33d6e",
|
||||
"rawTextureUuid": "050fb016-1a1f-4341-8367-283bfeddc4a8",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 4,
|
||||
"offsetY": -24.5,
|
||||
"trimX": 97,
|
||||
"trimY": 85,
|
||||
"width": 114,
|
||||
"height": 179,
|
||||
"rawWidth": 300,
|
||||
"rawHeight": 300,
|
||||
"offsetX": -11.5,
|
||||
"offsetY": 1.5,
|
||||
"trimX": 1,
|
||||
"trimY": 1,
|
||||
"width": 103,
|
||||
"height": 123,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 128,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "f1176719-d1d6-4af5-89c6-ddff16ab85fd",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
{"width":128,"SubTexture":[{"frameWidth":23,"y":44,"frameHeight":22,"width":22,"frameX":-1,"height":22,"name":"biu","frameY":0,"x":53},{"width":9,"y":68,"height":14,"name":"rightArm","x":76},{"frameWidth":29,"y":35,"frameHeight":32,"width":26,"frameX":-1,"height":32,"name":"yinmoqe00","frameY":0,"x":89},{"width":34,"y":1,"height":41,"name":"body","x":53},{"width":9,"y":44,"height":13,"name":"rightShoulder","x":77},{"width":19,"y":50,"height":18,"name":"rightFrontArm","x":23},{"width":14,"y":70,"height":14,"name":"rightHand","x":23},{"width":12,"y":68,"height":12,"name":"leftArm","x":62},{"width":13,"y":73,"height":12,"name":"leftShoulder","x":1},{"width":20,"y":50,"height":21,"name":"leftFrontArm","x":1},{"width":33,"y":1,"height":32,"name":"head","x":89},{"width":50,"y":1,"height":47,"name":"head2","x":1},{"width":16,"y":68,"height":14,"name":"leftHand","x":44},{"frameWidth":8,"y":59,"frameHeight":8,"width":4,"frameX":-1,"height":4,"name":"huomiao01","frameY":-2,"x":77}],"height":128,"name":"SoldierFireGhost","imagePath":"SoldierFireGhost_tex.png"}
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.0",
|
||||
"uuid": "4a9187d5-a9ad-4464-a03c-d2f3cc277051",
|
||||
"atlasJson": "{\"width\":128,\"SubTexture\":[{\"frameWidth\":23,\"y\":44,\"frameHeight\":22,\"width\":22,\"frameX\":-1,\"height\":22,\"name\":\"biu\",\"frameY\":0,\"x\":53},{\"width\":9,\"y\":68,\"height\":14,\"name\":\"rightArm\",\"x\":76},{\"frameWidth\":29,\"y\":35,\"frameHeight\":32,\"width\":26,\"frameX\":-1,\"height\":32,\"name\":\"yinmoqe00\",\"frameY\":0,\"x\":89},{\"width\":34,\"y\":1,\"height\":41,\"name\":\"body\",\"x\":53},{\"width\":9,\"y\":44,\"height\":13,\"name\":\"rightShoulder\",\"x\":77},{\"width\":19,\"y\":50,\"height\":18,\"name\":\"rightFrontArm\",\"x\":23},{\"width\":14,\"y\":70,\"height\":14,\"name\":\"rightHand\",\"x\":23},{\"width\":12,\"y\":68,\"height\":12,\"name\":\"leftArm\",\"x\":62},{\"width\":13,\"y\":73,\"height\":12,\"name\":\"leftShoulder\",\"x\":1},{\"width\":20,\"y\":50,\"height\":21,\"name\":\"leftFrontArm\",\"x\":1},{\"width\":33,\"y\":1,\"height\":32,\"name\":\"head\",\"x\":89},{\"width\":50,\"y\":1,\"height\":47,\"name\":\"head2\",\"x\":1},{\"width\":16,\"y\":68,\"height\":14,\"name\":\"leftHand\",\"x\":44},{\"frameWidth\":8,\"y\":59,\"frameHeight\":8,\"width\":4,\"frameX\":-1,\"height\":4,\"name\":\"huomiao01\",\"frameY\":-2,\"x\":77}],\"height\":128,\"name\":\"SoldierFireGhost\",\"imagePath\":\"SoldierFireGhost_tex.png\"}",
|
||||
"texture": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||
"subMetas": {}
|
||||
}
|
After Width: | Height: | Size: 8.3 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"SoldierFireGhost_tex": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "8ef8a6b3-0bac-4cf1-bba0-ab090f4d9e52",
|
||||
"rawTextureUuid": "700d963b-2192-4219-a066-8be5b3db7453",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -2.5,
|
||||
"offsetY": 21,
|
||||
"trimX": 1,
|
||||
"trimY": 1,
|
||||
"width": 121,
|
||||
"height": 84,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 128,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
7
frontend/assets/resources/map/dungeon.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "5650b341-a420-4d79-a969-39a461e13378",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
168
frontend/assets/resources/map/dungeon/map.tmx
Normal file
@@ -0,0 +1,168 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="128" height="128" tilewidth="16" tileheight="16" infinite="0" nextlayerid="3" nextobjectid="39">
|
||||
<tileset firstgid="1" source="tiles0.tsx"/>
|
||||
<tileset firstgid="65" source="tiles1.tsx"/>
|
||||
<tileset firstgid="129" source="tiles2.tsx"/>
|
||||
<layer id="2" name="Ground" width="128" height="128">
|
||||
<data encoding="base64" compression="zlib">
|
||||
eJzt2q1u22AYhuEo1UhBtYFWKh/bmRRMRYNFPZPxwcKi7jznSInkWk7teG5ex88FLhQD570d5/PP1WazuQIAAAAAAAAAAAAAAAAAgAmu96r3g/N5bjn0fw5U3aGy/3bv0P+p8XDEdoX0f9//GP3Xp93/YaD/9QJa6f95/fvov26773671zeb7jmhupX+5+2fQH/9qztU9v8yQXUz/fXXX3/9a/ovgf7666+//vrrf1n9/4zc7tue/uvq/9Z4PfLZ7vnz38ZP/cv7T53p4Z7i7/+kv/7667/bl/sO/dff/77T3+9ff/3r+38dMGf/Nv1r+w917+v/0vh1okN/67/l9B/b/nFj/X8pxvYf236r/0UZ6t/X+Efj5kj7bv+uxxPpX9e/r/1NS7f7Z/S/a3zXfzH9Pzrvt/vPTf/l9p+yRjyV/ufvP+YY6M5yzr5D5uy/XUCLJfafa8YfmdJe//X0r6Z/9jGgv/7661/doqp/9fyrpV//Vc+/mv7Z9M+mfzb9s+mfTf9s+mfTP5v+2fTPpn82/bPpn03/bPpn0z+b/tn0z6Z/Nv2z6Z/N+9/Z9M+mfzb//9n0z6Z/Nv2z6Z9N/2z6Z9M/m/7Z0vtT3wEAAAAAAAAAAAAAAAA4v39IY4NC
|
||||
</data>
|
||||
</layer>
|
||||
<objectgroup id="1" name="PlayerStartingPos">
|
||||
<object id="135" x="1090" y="833.667">
|
||||
<point/>
|
||||
</object>
|
||||
<object id="137" x="1215" y="830.5">
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
<objectgroup id="2" name="Barrier">
|
||||
<properties>
|
||||
<property name="type" value="barrier_and_shelter"/>
|
||||
</properties>
|
||||
<object id="8" x="648.242" y="480.606">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
|
||||
</object>
|
||||
<object id="9" x="650.667" y="1604.67">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,18.6667 1041.33,21.3333 1041.33,-1.33333"/>
|
||||
</object>
|
||||
<object id="10" x="634.485" y="505.455">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 4,1110 24,1110 24,-8"/>
|
||||
</object>
|
||||
<object id="11" x="1677.64" y="501.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 4,1110 24,1110 24,-8"/>
|
||||
</object>
|
||||
<object id="14" x="688.667" y="464">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -0.666667,78 33.3333,78 32,-0.666667"/>
|
||||
</object>
|
||||
<object id="15" x="833.333" y="495.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -112,1.33333 -111.333,44.6667 -1.33333,44.6667"/>
|
||||
</object>
|
||||
<object id="17" x="832" y="574">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -67.3333,0 -67.3333,-76.6667 0.666667,-76"/>
|
||||
</object>
|
||||
<object id="18" x="865.333" y="606.667">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -210,-0.666667 -210,143.333 0,142.667"/>
|
||||
</object>
|
||||
<object id="19" x="754.667" y="1055.33">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="-2,1.33333 -100,0.666667 -97.3333,-454 -9.33333,-451.333"/>
|
||||
</object>
|
||||
<object id="20" x="769.333" y="747.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -115.333,0.666667 -114.667,160.667 -2,162"/>
|
||||
</object>
|
||||
<object id="21" x="768" y="960.667">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -18,0.666667 -18.6667,95.3333 0,95.3333"/>
|
||||
</object>
|
||||
<object id="23" x="786" y="1058.67">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,-52.6667 -20,-52 -19.3333,-1.33333"/>
|
||||
</object>
|
||||
<object id="24" x="1118.67" y="749.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -0.666667,-94 -254,-93.3333 -256.667,1.33333"/>
|
||||
</object>
|
||||
<object id="25" x="1168" y="975.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,16.6667 224.667,17.3333 225.333,-2"/>
|
||||
</object>
|
||||
<object id="28" x="1394.67" y="958">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -210.667,1.33333 -210.667,17.3333 -2,18"/>
|
||||
</object>
|
||||
<object id="29" x="1119" y="654.5">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,63.5 272.5,65 273,-1"/>
|
||||
</object>
|
||||
<object id="30" x="1136.5" y="717.5">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -0.5,17 255,17.5 255,1.5"/>
|
||||
</object>
|
||||
<object id="31" x="1152" y="735.667">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,15 80.3333,15.3333 80.3333,-2"/>
|
||||
</object>
|
||||
<object id="32" x="1280.67" y="734.667">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -0.666667,64.3333 48,65 48,0"/>
|
||||
</object>
|
||||
<object id="34" x="1329" y="783">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 63.3333,0.333333 63,-48.3333 -0.666667,-48.3333"/>
|
||||
</object>
|
||||
<object id="35" x="1296.67" y="799">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -0.666667,31.3333 31.3333,31.6667 31.3333,-0.333333"/>
|
||||
</object>
|
||||
<object id="36" x="1280" y="848">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0.333333,62 112,63 111.667,-1.33333"/>
|
||||
</object>
|
||||
<object id="37" x="1392.33" y="911.333">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 -81.3333,-0.666667 -81,45 -0.666667,46.3333"/>
|
||||
</object>
|
||||
<object id="38" x="1344.33" y="800.667">
|
||||
<properties>
|
||||
<property name="boundary_type" value="barrier"/>
|
||||
</properties>
|
||||
<polyline points="0,0 0,46.3333 47,46.3333 47,-1"/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
5
frontend/assets/resources/map/dungeon/map.tmx.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ver": "2.0.2",
|
||||
"uuid": "1b802c87-1978-4c6a-bd0b-1f6b8526b3ad",
|
||||
"subMetas": {}
|
||||
}
|
4
frontend/assets/resources/map/dungeon/tiles0.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.2" tiledversion="1.2.3" name="tiles0" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||
<image source="watabou_pixel_dungeon_orig_files/tiles0.png" width="256" height="64"/>
|
||||
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles0.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ver": "2.0.0",
|
||||
"uuid": "d4cf0e72-7454-4310-b975-beb5e81a63ae",
|
||||
"subMetas": {}
|
||||
}
|
4
frontend/assets/resources/map/dungeon/tiles1.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.2" tiledversion="1.2.3" name="tiles1" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||
<image source="watabou_pixel_dungeon_orig_files/tiles1.png" width="256" height="64"/>
|
||||
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles1.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ver": "2.0.0",
|
||||
"uuid": "0bcabaac-a406-4b3d-9285-814e00c5b09d",
|
||||
"subMetas": {}
|
||||
}
|
4
frontend/assets/resources/map/dungeon/tiles2.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.2" tiledversion="1.2.3" name="tiles2" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||
<image source="watabou_pixel_dungeon_orig_files/tiles2.png" width="256" height="64"/>
|
||||
</tileset>
|
5
frontend/assets/resources/map/dungeon/tiles2.tsx.meta
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ver": "2.0.0",
|
||||
"uuid": "2e53500f-f9c8-4b5b-b74c-f2adbc2ec34d",
|
||||
"subMetas": {}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"uuid": "2a231040-0e69-42b4-a35b-9690e976e71a",
|
||||
"isSubpackage": false,
|
||||
"subpackageName": "",
|
||||
"subMetas": {}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"ver": "2.0.0",
|
||||
"uuid": "acb40b5d-372b-4502-a6bf-f756908f8221",
|
||||
"subMetas": {}
|
||||
}
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "d8e6c175-1f17-48df-a0aa-cdd9785f4d3a",
|
||||
"uuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
@@ -9,21 +9,21 @@
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"Tile_W256_H256_S01": {
|
||||
"amulet": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "4a23290b-bf5a-4849-ac19-6ebd4b7daa59",
|
||||
"rawTextureUuid": "d8e6c175-1f17-48df-a0aa-cdd9785f4d3a",
|
||||
"uuid": "52580025-ae7a-4c48-878f-f685bc90e737",
|
||||
"rawTextureUuid": "94f42afa-43e1-4936-b5ac-7bcfe252f9e1",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 64,
|
||||
"width": 1280,
|
||||
"height": 896,
|
||||
"rawWidth": 1280,
|
||||
"rawHeight": 1024,
|
||||
"trimY": 0,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"rawWidth": 32,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
After Width: | Height: | Size: 189 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "136d09e9-c33c-45dc-abb7-e367d730c814",
|
||||
"uuid": "a9781031-1dba-4214-9167-080fc1920f92",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
@@ -9,21 +9,21 @@
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"Tile_W256_H128_S01": {
|
||||
"arcs1": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "7acc48f5-d9c9-4438-8794-57a85590bd97",
|
||||
"rawTextureUuid": "136d09e9-c33c-45dc-abb7-e367d730c814",
|
||||
"uuid": "5430edd4-3e0b-4509-a575-d7c7f8db4e31",
|
||||
"rawTextureUuid": "a9781031-1dba-4214-9167-080fc1920f92",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 831,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 2048,
|
||||
"height": 386,
|
||||
"rawWidth": 2048,
|
||||
"rawHeight": 2048,
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"rawWidth": 32,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
After Width: | Height: | Size: 288 B |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "74245e28-6cec-4960-ac41-5b482ad8fd13",
|
||||
"uuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
@@ -9,21 +9,21 @@
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"Tile_W256_H256_S02": {
|
||||
"arcs2": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "8fc46c1f-6fb4-4290-99f3-b773b92312b7",
|
||||
"rawTextureUuid": "74245e28-6cec-4960-ac41-5b482ad8fd13",
|
||||
"uuid": "11915948-c7bf-41db-b6eb-74b364df7688",
|
||||
"rawTextureUuid": "940f2fcc-df03-46ed-ad32-d92880efe501",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 19.5,
|
||||
"offsetY": 3.5,
|
||||
"trimX": 89,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 1409,
|
||||
"height": 251,
|
||||
"rawWidth": 1548,
|
||||
"rawHeight": 258,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"rawWidth": 64,
|
||||
"rawHeight": 64,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"avatars": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "b472e0d1-c866-4a4d-b8f9-4daca275b8db",
|
||||
"rawTextureUuid": "ac193c85-fb5d-4d66-b911-81e2254e0009",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -16.5,
|
||||
"offsetY": 2,
|
||||
"trimX": 2,
|
||||
"trimY": 0,
|
||||
"width": 91,
|
||||
"height": 28,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"badges": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "fec2a102-8c77-4528-8130-488184e8bf38",
|
||||
"rawTextureUuid": "f204f568-cf6e-462e-834e-ef6961dae26e",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 128,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "90cc6214-a196-4c59-885e-86c4449d426b",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"banners": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "ce394cd8-e574-4170-8c9c-24072e7d6428",
|
||||
"rawTextureUuid": "90cc6214-a196-4c59-885e-86c4449d426b",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 2.5,
|
||||
"offsetY": 16.5,
|
||||
"trimX": 5,
|
||||
"trimY": 4,
|
||||
"width": 123,
|
||||
"height": 215,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 256,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "e0a44b92-2564-401c-8276-b47e279c0039",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"bat": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "5ad7b260-8239-4eb0-a436-7bc1c3743433",
|
||||
"rawTextureUuid": "e0a44b92-2564-401c-8276-b47e279c0039",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -12,
|
||||
"offsetY": 0.5,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 104,
|
||||
"height": 15,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "a83b9bc0-78f0-41ed-acc2-6f88020d4828",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"bee": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "a5d1dc1d-5f46-430c-96d3-3e2d2584be13",
|
||||
"rawTextureUuid": "a83b9bc0-78f0-41ed-acc2-6f88020d4828",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -40.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 2,
|
||||
"trimY": 0,
|
||||
"width": 171,
|
||||
"height": 16,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 918 B |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "4ba0be3a-32d0-43f1-9640-5fb0f65ea16a",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"blacksmith": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "cc238a21-5d38-496a-86c0-3c223cb0977a",
|
||||
"rawTextureUuid": "4ba0be3a-32d0-43f1-9640-5fb0f65ea16a",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -6.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 51,
|
||||
"height": 16,
|
||||
"rawWidth": 64,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "2ed4fd6c-7c83-49e8-baba-01145a109cc8",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"brute": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "eb04bd2a-243f-4ca8-8e58-3bc6cc5e61c1",
|
||||
"rawTextureUuid": "2ed4fd6c-7c83-49e8-baba-01145a109cc8",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -61.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 131,
|
||||
"height": 32,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "f38d420a-e1d6-4b6f-8863-e35c52cf4f8b",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"buffs": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "5a9620a1-2da1-4870-94e9-096e5e323a43",
|
||||
"rawTextureUuid": "f38d420a-e1d6-4b6f-8863-e35c52cf4f8b",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 128,
|
||||
"height": 16,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "4ce8b218-cd3d-40d8-9102-ba1e4cf415eb",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"burning_fist": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "c62848ac-4eec-46f9-b684-b5365d507212",
|
||||
"rawTextureUuid": "4ce8b218-cd3d-40d8-9102-ba1e4cf415eb",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -42.5,
|
||||
"offsetY": 7.5,
|
||||
"trimX": 3,
|
||||
"trimY": 0,
|
||||
"width": 165,
|
||||
"height": 17,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "21e86a32-9ad1-4d02-877b-37e65b874697",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"chrome": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "ba373151-5747-4c0d-8457-5543fefd50d2",
|
||||
"rawTextureUuid": "21e86a32-9ad1-4d02-877b-37e65b874697",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -21,
|
||||
"offsetY": 0.5,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 84,
|
||||
"height": 63,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 64,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "cde66edb-eafb-4e46-b5a9-0b21851f4974",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"crab": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "fc11e3df-a336-4ee6-b9f7-1dbabb1c7b1b",
|
||||
"rawTextureUuid": "cde66edb-eafb-4e46-b5a9-0b21851f4974",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -15.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 223,
|
||||
"height": 16,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "b146d7f8-393b-42d1-bb53-f0f4ad379dfc",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"dashboard": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "4d4eb3aa-5d9e-4f78-9c0c-dcb99f479c2a",
|
||||
"rawTextureUuid": "b146d7f8-393b-42d1-bb53-f0f4ad379dfc",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -0.5,
|
||||
"offsetY": 0,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 127,
|
||||
"height": 32,
|
||||
"rawWidth": 128,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "6abab25f-0d0a-4994-8001-bc37e7632abe",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"demon": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "31fcc6ba-8e07-47f7-a933-0f45384dc511",
|
||||
"rawTextureUuid": "6abab25f-0d0a-4994-8001-bc37e7632abe",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -2,
|
||||
"offsetY": 1,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 58,
|
||||
"height": 14,
|
||||
"rawWidth": 64,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "902e544c-494e-4d24-989c-8101b081d516",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"dm300": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "e18e981b-7790-45be-a877-fa1ffccd9cfd",
|
||||
"rawTextureUuid": "902e544c-494e-4d24-989c-8101b081d516",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -29,
|
||||
"offsetY": 6,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 198,
|
||||
"height": 20,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "84e847c4-c2d1-4d77-8b62-439c7cc10d16",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"effects": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "c0abc05f-e714-47bf-8c8a-95a26b09cadb",
|
||||
"rawTextureUuid": "84e847c4-c2d1-4d77-8b62-439c7cc10d16",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": 0,
|
||||
"offsetY": 4.5,
|
||||
"trimX": 0,
|
||||
"trimY": 0,
|
||||
"width": 32,
|
||||
"height": 23,
|
||||
"rawWidth": 32,
|
||||
"rawHeight": 32,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"ver": "2.3.3",
|
||||
"uuid": "8c4dbca2-14e2-4ef6-9544-ee04f69bdaaa",
|
||||
"type": "sprite",
|
||||
"wrapMode": "clamp",
|
||||
"filterMode": "bilinear",
|
||||
"premultiplyAlpha": false,
|
||||
"genMipmaps": false,
|
||||
"packable": true,
|
||||
"platformSettings": {},
|
||||
"subMetas": {
|
||||
"elemental": {
|
||||
"ver": "1.0.4",
|
||||
"uuid": "1da68822-1fd7-49fc-b855-6c5fbf8a9af3",
|
||||
"rawTextureUuid": "8c4dbca2-14e2-4ef6-9544-ee04f69bdaaa",
|
||||
"trimType": "auto",
|
||||
"trimThreshold": 1,
|
||||
"rotated": false,
|
||||
"offsetX": -45.5,
|
||||
"offsetY": 1,
|
||||
"trimX": 1,
|
||||
"trimY": 0,
|
||||
"width": 163,
|
||||
"height": 14,
|
||||
"rawWidth": 256,
|
||||
"rawHeight": 16,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"subMetas": {}
|
||||
}
|
||||
}
|
||||
}
|