代码初始化
This commit is contained in:
1 parent
8160eca074
commit
dffebce01d
43 files changed
+6383
No files matched your search
Whitespace-only changes.
+666
@@ -0,0 +1,666 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import mod.client.extraClientApi as clientApi
|
||||
from mod.common.utils.mcmath import Matrix
|
||||
from config.PlayConfig import PlayConfig
|
||||
from utils.SafeEval import SafeEval
|
||||
from .utils.CatmullRom import getCatmullromLerpFromKeyframe
|
||||
from exception.NotPlayConfigException import NotPlayConfigException
|
||||
from exception.NotClientSystemException import NotClientSystemException
|
||||
|
||||
compFactory = clientApi.GetEngineCompFactory()
|
||||
levelId = clientApi.GetLevelId()
|
||||
|
||||
|
||||
class Camera(object):
|
||||
# 全局摄像机对象(用来判断当前客户端是否有摄像机正在播放)
|
||||
globalCameraObj = None
|
||||
|
||||
def __init__(self, client, animationName, path='modconfigs/spring_camera_animations.json', cameraName='camera',
|
||||
animationDict=None, speed=1.0):
|
||||
"""
|
||||
构造方法
|
||||
|
||||
Args:
|
||||
client (ClientSystem): 客户端系统对象
|
||||
animationName (str): 动画在文件中的名称
|
||||
path (str, optional): 动画文件路径,需要包含 ".json"。默认值 'modconfigs/spring_camera_animations.json'
|
||||
cameraName (str, optional): 动画 JSON 中的摄像机名称。默认值 'camera'
|
||||
animationDict (dict, optional): 动画关键帧字典。默认值 None
|
||||
speed (float, optional): 动画播放速度倍数。默认值 1.0
|
||||
"""
|
||||
if not isinstance(client, clientApi.GetClientSystemCls()):
|
||||
raise NotClientSystemException("传入 Camera 内的 client 不是一个 ClientSystem")
|
||||
|
||||
# region 初始化变量
|
||||
self.client = client
|
||||
self.eval = SafeEval()
|
||||
# 动画总时长
|
||||
self.animationLength = None
|
||||
# 动画旋转关键帧数组
|
||||
self.animationRotationArray = None
|
||||
# 动画位置关键帧数组
|
||||
self.animationPositionArray = None
|
||||
# 动画旋转关键帧数组的索引
|
||||
self.rotationIndex = None
|
||||
# 动画位置关键帧数组的索引
|
||||
self.positionIndex = None
|
||||
# 动画开始时间(单位:秒)
|
||||
self.startAnimationTime = None
|
||||
# 上一个帧的时间(单位:秒)
|
||||
self.preAnimationTime = None
|
||||
# 上一帧的旋转关键帧参数,形如:(0.0, [0, 180, 0], 'catmullrom')
|
||||
self.preAnimationRotArgs = None
|
||||
# 上一帧的位置关键帧参数,形如:(0.0, [0, 180, 0], 'catmullrom')
|
||||
self.preAnimationPosArgs = None
|
||||
# 动画开始前的摄像机旋转参数
|
||||
self.rotationOrigin = None
|
||||
# 动画开始前的摄像机锚点参数
|
||||
self.positionOrigin = None
|
||||
# play 配置对象
|
||||
self.playConfig = PlayConfig()
|
||||
# 是否正在播放摄像机动画
|
||||
self.isPlaying = False
|
||||
# 摄像机接口对象
|
||||
self.cameraCompFactory = compFactory.CreateCamera(levelId)
|
||||
# 绕 y 轴旋转的旋转矩阵
|
||||
self.leftRotYMat = None
|
||||
# 是否覆盖当前的位置和旋转(json 配置)
|
||||
self.overridePreviousAnimation = False
|
||||
# 动画循环状态:播放一次、停止于最后一帧、循环播放
|
||||
self.loop = None
|
||||
# 防止停不住
|
||||
self.playTimer = None
|
||||
# 前一帧设置的摄像机旋转角度(目前仅用于将当前帧设置的旋转角度传递给后续的 GameRenderTickEvent 事件)
|
||||
self.beforeRot = None
|
||||
# 是否正在暂停
|
||||
self.isPausing = False
|
||||
# 动画暂停开始时间
|
||||
self.pauseStartTime = 0.0
|
||||
# endregion
|
||||
|
||||
# 动画关键帧字典
|
||||
if not animationDict:
|
||||
animationDict = clientApi.GetModConfigJson(path)['animations'][animationName]
|
||||
self.animationLength = animationDict.get('animation_length')
|
||||
if not self.animationLength:
|
||||
# 无时间的动画,默认动画时长 24 小时
|
||||
self.animationLength = 86400.0
|
||||
|
||||
# 应用速度倍数
|
||||
speed = 1.0 / speed
|
||||
self.animationLength *= speed
|
||||
|
||||
self.overridePreviousAnimation = animationDict['override_previous_animation'] if animationDict.__contains__(
|
||||
'override_previous_animation') else False
|
||||
self.loop = animationDict['loop'] if animationDict.__contains__('loop') else False
|
||||
|
||||
if animationDict['bones'][cameraName].__contains__('rotation'):
|
||||
animationRotationDict = animationDict['bones'][cameraName]['rotation']
|
||||
# 解析只有单个关键帧的动画
|
||||
if isinstance(animationRotationDict, list):
|
||||
animationRotationDict = {'0.0': animationRotationDict}
|
||||
# 动画旋转关键帧数组
|
||||
self.animationRotationArray = sorted(animationRotationDict.items(), key=lambda item: float(item[0]))
|
||||
self.animationRotationArray = [
|
||||
(float(item[0]) * speed,
|
||||
item[1]['post'] if isinstance(item[1], dict) else item[1],
|
||||
item[1].get('lerp_mode') if isinstance(item[1], dict) else None)
|
||||
for item in self.animationRotationArray
|
||||
]
|
||||
self._parseRotKeyframe(self.animationRotationArray)
|
||||
|
||||
if animationDict['bones'][cameraName].__contains__('position'):
|
||||
animationPositionDict = animationDict['bones'][cameraName]['position']
|
||||
# 解析只有单个关键帧的动画
|
||||
if isinstance(animationPositionDict, list):
|
||||
animationPositionDict = {'0.0': animationPositionDict}
|
||||
# 动画位置关键帧数组
|
||||
self.animationPositionArray = sorted(animationPositionDict.items(), key=lambda item: float(item[0]))
|
||||
self.animationPositionArray = [
|
||||
(float(item[0]) * speed,
|
||||
item[1]['post'] if isinstance(item[1], dict) else item[1],
|
||||
item[1].get('lerp_mode') if isinstance(item[1], dict) else None)
|
||||
for item in self.animationPositionArray
|
||||
]
|
||||
self._parsePosKeyframe(self.animationPositionArray)
|
||||
|
||||
def _parseRotKeyframe(self, animationArray):
|
||||
"""
|
||||
转换旋转关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 旋转关键帧数组
|
||||
"""
|
||||
rot = compFactory.CreateCamera(levelId).GetCameraRotation()
|
||||
self._parseKeyframe(animationArray, rot)
|
||||
|
||||
def _parsePosKeyframe(self, animationArray):
|
||||
"""
|
||||
转换位置关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 位置关键帧数组
|
||||
"""
|
||||
pos = compFactory.CreatePos(clientApi.GetLocalPlayerId()).GetFootPos()
|
||||
self._parseKeyframe(animationArray, (pos[0] * 16.0, pos[1] * 16.0, pos[2] * 16.0))
|
||||
|
||||
def _parseKeyframe(self, animationArray, replaceThis):
|
||||
"""
|
||||
转换关键帧中的字符串
|
||||
|
||||
Args:
|
||||
animationArray (list): 关键帧数组
|
||||
replaceThis (tuple): 替换 this 的元组
|
||||
"""
|
||||
for i in xrange(0, len(animationArray)):
|
||||
for j in xrange(0, len(animationArray[i][1])):
|
||||
if not isinstance(animationArray[i][1][j], str):
|
||||
continue
|
||||
if 'this' in animationArray[i][1][j]:
|
||||
animationArray[i][1][j] = animationArray[i][1][j].replace('this', format(replaceThis[j], '.16f'))
|
||||
animationArray[i][1][j] = self.eval.evaluate(animationArray[i][1][j])
|
||||
elif 'math.random' in animationArray[i][1][j]:
|
||||
match = re.search(r'(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)', animationArray[i][1][j])
|
||||
if match:
|
||||
animationArray[i][1][j] = random.uniform(float(match.group(1)), float(match.group(3)))
|
||||
|
||||
def _indexOutEnd(self):
|
||||
"""
|
||||
防止下标越界
|
||||
"""
|
||||
# 如果数组下标越界,设置下标为最后一个元素
|
||||
if self.animationRotationArray and self.rotationIndex >= len(self.animationRotationArray):
|
||||
self.rotationIndex = len(self.animationRotationArray) - 1
|
||||
if self.animationPositionArray and self.positionIndex >= len(self.animationPositionArray):
|
||||
self.positionIndex = len(self.animationPositionArray) - 1
|
||||
|
||||
def _leftRotationMatrixY(self, angleDegrees):
|
||||
"""
|
||||
生成绕 Y 轴旋转指定角度的 3x3 旋转矩阵(左手坐标系)
|
||||
|
||||
Args:
|
||||
angleDegrees (float): 旋转角度
|
||||
|
||||
Returns:
|
||||
Matrix: 3x3 旋转矩阵
|
||||
"""
|
||||
# 将角度转换为弧度
|
||||
angleRad = math.radians(angleDegrees)
|
||||
|
||||
# 计算余弦和正弦值
|
||||
cosAngle = math.cos(angleRad)
|
||||
sinAngle = math.sin(angleRad)
|
||||
|
||||
# 构建绕 Y 轴旋转矩阵(左手坐标系)
|
||||
rotationMatrix = Matrix.Create([
|
||||
[cosAngle, 0, -sinAngle],
|
||||
[0, 1, 0, ],
|
||||
[sinAngle, 0, cosAngle]
|
||||
])
|
||||
|
||||
return rotationMatrix
|
||||
|
||||
def _leftRotationMatrixX(self, angleDegrees):
|
||||
"""
|
||||
生成绕 X 轴旋转指定角度的 3x3 旋转矩阵(左手坐标系)
|
||||
|
||||
Args:
|
||||
angleDegrees (float): 旋转角度
|
||||
|
||||
Returns:
|
||||
Matrix: 3x3 旋转矩阵
|
||||
"""
|
||||
# 将角度转换为弧度
|
||||
angleRad = math.radians(angleDegrees)
|
||||
|
||||
# 计算余弦和正弦值
|
||||
cosAngle = math.cos(angleRad)
|
||||
sinAngle = math.sin(angleRad)
|
||||
|
||||
# 构建绕 X 轴旋转矩阵(左手坐标系)
|
||||
rotationMatrix = Matrix.Create([
|
||||
[1, 0, 0],
|
||||
[0, cosAngle, sinAngle],
|
||||
[0, -sinAngle, cosAngle]
|
||||
])
|
||||
|
||||
return rotationMatrix
|
||||
|
||||
def _getNowTime(self):
|
||||
"""
|
||||
获取当前时间
|
||||
"""
|
||||
if self.isPausing and self.preAnimationTime is not None:
|
||||
return self.preAnimationTime
|
||||
return time.time()
|
||||
|
||||
def playFirstPerson(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(允许拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, True, True, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(允许拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, True, False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableDrag(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(禁止拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, False, True, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playFirstPersonDisableDragWithMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第一人称动画(禁止拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(True, False, False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPerson(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(允许拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, True, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(允许拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, True, False, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableDrag(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(禁止拖拽,允许移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, False, True, playAfterFunc=playAfterFunc)
|
||||
|
||||
def playThirdPersonDisableDragWithMove(self, playAfterFunc=None):
|
||||
"""
|
||||
播放第三人称动画(禁止拖拽,禁止移动)
|
||||
|
||||
Args:
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.play(False, False, False, playAfterFunc=playAfterFunc)
|
||||
|
||||
def play(self, isFirstPerson=True, isAllowDrag=True, isAllowMove=True, isPosFollowXRot=True, isPosFollowYRot=True,
|
||||
isZRotForceReturn=True, isZRotStartOrigin=False,
|
||||
isDelayOneFrame=False, isLockPerspective=False, tickPosFunc=None, tickRotFunc=None, playAfterFunc=None):
|
||||
"""
|
||||
播放摄像机动画
|
||||
|
||||
Args:
|
||||
isFirstPerson (bool, optional): 是否是第一人称动画。如果是,那么 Y 轴旋转会减 180 度。默认值 False
|
||||
isAllowDrag (bool, optional): 是否允许玩家拖拽视角。默认值 False
|
||||
isAllowMove (bool, optional): 是否允许玩家移动。默认值 False
|
||||
isPosFollowXRot (bool, optional): 摄像机空间坐标是否受 X 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isPosFollowYRot (bool, optional): 摄像机空间坐标是否受 Y 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isZRotForceReturn (bool, optional): 是否在动画播放完成后强制归零 Z 轴的旋转值。默认值 True
|
||||
isZRotStartOrigin (bool, optional): 是否将动画播放前的 Z 轴旋转值作为初始的 Z 轴旋转值。置为 False 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常(json 配置 override_previous_animation 值为 True 时无效)。默认值 False
|
||||
isDelayOneFrame (bool, optional): 是否延迟一帧再播放摄像机动画。默认值 True
|
||||
isLockPerspective (bool, optional): 是否锁定并强制切换玩家视角。默认值 True
|
||||
tickPosFunc (function, optional): 摄像机动画播放时,在每次设置摄像机位置前执行的函数。默认值 None
|
||||
tickRotFunc (function, optional): 摄像机动画播放时,在每次设置摄像机旋转前执行的函数。默认值 None
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.playWithConfig(
|
||||
PlayConfig(isFirstPerson, isAllowDrag, isAllowMove, isPosFollowXRot, isPosFollowYRot, isZRotForceReturn,
|
||||
isZRotStartOrigin, isDelayOneFrame, isLockPerspective,
|
||||
tickPosFunc, tickRotFunc, playAfterFunc))
|
||||
|
||||
def playWithConfig(self, config):
|
||||
"""
|
||||
使用播放配置对象播放摄像机动画
|
||||
|
||||
Args:
|
||||
config (PlayConfig): 播放配置对象
|
||||
"""
|
||||
if not isinstance(config, PlayConfig):
|
||||
raise NotPlayConfigException("传入 playWithConfig 内的 config 不是一个 PlayConfig")
|
||||
|
||||
if Camera.globalCameraObj:
|
||||
# 强制停止正在播放的摄像机动画(全局)
|
||||
Camera.globalCameraObj.stop(True)
|
||||
|
||||
# 开始监听
|
||||
from .CameraListen import _enable
|
||||
if not _enable:
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.GameRenderTickEvent, 10)
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"HealthChangeClientEvent", self, self.HealthChangeClientEvent)
|
||||
|
||||
def realPlay():
|
||||
# 初始化相关变量
|
||||
self.playConfig = config
|
||||
self.rotationIndex = 0
|
||||
self.positionIndex = 0
|
||||
operationComp = compFactory.CreateOperation(levelId)
|
||||
# 不允许玩家移动
|
||||
if not self.playConfig.isAllowMove:
|
||||
operationComp.SetCanMove(False)
|
||||
operationComp.SetCanJump(False)
|
||||
operationComp.SetCanInair(False)
|
||||
# 不允许玩家拖拽
|
||||
if not self.playConfig.isAllowDrag:
|
||||
operationComp.SetCanDrag(False)
|
||||
# 不允许玩家切换视角
|
||||
if self.playConfig.isLockPerspective:
|
||||
operationComp.SetCanPerspective(False)
|
||||
# 第三人称
|
||||
if not self.playConfig.isFirstPerson:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(1)
|
||||
self.cameraCompFactory.SetCameraDistanceFixed(True)
|
||||
self.cameraCompFactory.DepartCamera()
|
||||
self.cameraCompFactory.SetCameraOffset((0, 0.01, -4))
|
||||
else:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(0)
|
||||
if self.animationRotationArray:
|
||||
self.preAnimationRotArgs = (float(self.animationRotationArray[self.rotationIndex][0]),
|
||||
self.animationRotationArray[self.rotationIndex][1])
|
||||
if self.animationPositionArray:
|
||||
self.preAnimationPosArgs = (float(self.animationPositionArray[self.positionIndex][0]),
|
||||
self.animationPositionArray[self.positionIndex][1])
|
||||
self.preAnimationTime = self.startAnimationTime = time.time()
|
||||
# 是否覆盖
|
||||
if self.overridePreviousAnimation:
|
||||
self.rotationOrigin = (0, 0, 0)
|
||||
self.positionOrigin = (0, 0, 0)
|
||||
else:
|
||||
self.rotationOrigin = self.cameraCompFactory.GetCameraRotation()
|
||||
self.positionOrigin = self.cameraCompFactory.GetCameraAnchor()
|
||||
# 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常
|
||||
if not self.playConfig.isZRotStartOrigin:
|
||||
self.rotationOrigin = (self.rotationOrigin[0], self.rotationOrigin[1], 0.0)
|
||||
# 绕 y 轴的旋转矩阵
|
||||
self.leftRotYMat = self._leftRotationMatrixY(self.rotationOrigin[1])
|
||||
# 一定要在最后给 isPlaying 赋值
|
||||
Camera.globalCameraObj = self
|
||||
self.isPlaying = True
|
||||
|
||||
if config.isDelayOneFrame:
|
||||
# 延时一帧再播放(为了确保上一个动画完全停止,使得下一次获取初始数据时不受上次动画影响)
|
||||
compGame = compFactory.CreateGame(levelId)
|
||||
self.playTimer = compGame.AddTimer(0.0, realPlay)
|
||||
else:
|
||||
realPlay()
|
||||
|
||||
def destroyListen(self):
|
||||
"""
|
||||
销毁内置监听
|
||||
"""
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"GameRenderTickEvent", self, self.GameRenderTickEvent)
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(),
|
||||
"HealthChangeClientEvent", self, self.HealthChangeClientEvent)
|
||||
|
||||
def stop(self, isStopLoop=False):
|
||||
"""
|
||||
停止摄像机动画
|
||||
|
||||
Args:
|
||||
isStopLoop (bool, optional): 是否停止循环动画(仅对 json 中 loop 配置为 True 的动画有效)。默认值 False
|
||||
"""
|
||||
if self.playTimer: compFactory.CreateGame(levelId).CancelTimer(self.playTimer)
|
||||
|
||||
# 取消监听
|
||||
from .CameraListen import _enable
|
||||
if not _enable:
|
||||
self.destroyListen()
|
||||
|
||||
self.isPlaying = False
|
||||
self.isPausing = False
|
||||
Camera.globalCameraObj = None
|
||||
|
||||
# region 摄像机还原
|
||||
# 第三人称
|
||||
if not self.playConfig.isFirstPerson:
|
||||
if self.playConfig.isLockPerspective:
|
||||
compFactory.CreatePlayerView(clientApi.GetLocalPlayerId()).SetPerspective(0)
|
||||
self.cameraCompFactory.SetCameraDistanceFixed(False)
|
||||
self.cameraCompFactory.UnDepartCamera()
|
||||
self.cameraCompFactory.SetCameraOffset((0, 0, 0))
|
||||
|
||||
# 根据 loop 的值进行设置
|
||||
nowCameraRotation = self.rotationOrigin
|
||||
nowCameraPosition = self.positionOrigin
|
||||
if self.loop == 'hold_on_last_frame' or self.loop == True or self.playConfig.isAllowDrag:
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
if not self.playConfig.isAllowDrag:nowCameraPosition = self.cameraCompFactory.GetCameraAnchor()
|
||||
|
||||
# 是否强制回正 Z 轴
|
||||
if self.playConfig.isZRotForceReturn:
|
||||
self.cameraCompFactory.SetCameraRotation((nowCameraRotation[0], nowCameraRotation[1], 0))
|
||||
else:
|
||||
self.cameraCompFactory.SetCameraRotation(nowCameraRotation)
|
||||
self.cameraCompFactory.SetCameraAnchor(nowCameraPosition)
|
||||
# endregion
|
||||
|
||||
# 判断是否循环播放
|
||||
if self.loop == True and not isStopLoop:
|
||||
self.playWithConfig(self.playConfig)
|
||||
return
|
||||
|
||||
operationComp = compFactory.CreateOperation(levelId)
|
||||
# 恢复玩家移动
|
||||
operationComp.SetCanMove(True)
|
||||
operationComp.SetCanJump(True)
|
||||
operationComp.SetCanInair(True)
|
||||
operationComp.SetCanDrag(True)
|
||||
# 允许玩家切换视角
|
||||
operationComp.SetCanPerspective(True)
|
||||
|
||||
# 动画停止播放后执行传入的回调函数
|
||||
if (self.loop == True and isStopLoop) or (self.loop != True):
|
||||
if callable(self.playConfig.playAfterFunc):
|
||||
self.playConfig.playAfterFunc()
|
||||
|
||||
def pause(self, resumeTime=0):
|
||||
# type: (float) -> None
|
||||
"""
|
||||
暂停摄像机动画
|
||||
"""
|
||||
if self.isPausing:
|
||||
return
|
||||
self.isPausing = True
|
||||
self.pauseStartTime = time.time()
|
||||
|
||||
# 多少秒后恢复动画播放
|
||||
if resumeTime > 0:
|
||||
compFactory.CreateGame(levelId).AddTimer(resumeTime, self.resume)
|
||||
|
||||
def resume(self):
|
||||
"""
|
||||
继续播放摄像机动画
|
||||
"""
|
||||
if not self.isPausing:
|
||||
return
|
||||
self.isPausing = False
|
||||
pauseTime = time.time() - self.pauseStartTime
|
||||
self.startAnimationTime += pauseTime
|
||||
self.preAnimationTime += pauseTime
|
||||
|
||||
def GameRenderTickEvent(self, event):
|
||||
"""
|
||||
每 Tick 触发
|
||||
"""
|
||||
if not self.isPlaying:
|
||||
return
|
||||
nowAnimationTime = self._getNowTime()
|
||||
startToNowTime = nowAnimationTime - self.startAnimationTime # 当前时间距离动画开始的时间
|
||||
preToNowTime = nowAnimationTime - self.preAnimationTime # 当前时间距离上一帧的时间
|
||||
self.preAnimationTime = nowAnimationTime # 更新上一帧的时间为当前时间,为下一次计算做准备
|
||||
|
||||
# 如果自动画开始以来经过的时间,大于动画总时长,则动画结束
|
||||
if startToNowTime > self.animationLength:
|
||||
self.stop()
|
||||
return
|
||||
|
||||
self._indexOutEnd()
|
||||
# 如果自动画开始以来经过的时间,大于当前关键帧时间,说明关键帧索引应该增加
|
||||
while self.animationRotationArray and self.rotationIndex < len(
|
||||
self.animationRotationArray) and startToNowTime > float(
|
||||
self.animationRotationArray[self.rotationIndex][0]):
|
||||
# 1.0.3 修复上个关键帧没执行到结尾就计算下个关键帧所带来的问题
|
||||
self.preAnimationRotArgs = (self.preAnimationRotArgs[0], self.animationRotationArray[self.rotationIndex][1])
|
||||
self.rotationIndex += 1
|
||||
while self.animationPositionArray and self.positionIndex < len(
|
||||
self.animationPositionArray) and startToNowTime > float(
|
||||
self.animationPositionArray[self.positionIndex][0]):
|
||||
# 1.0.3 修复上个关键帧没执行到结尾就计算下个关键帧所带来的问题
|
||||
self.preAnimationPosArgs = (self.preAnimationPosArgs[0], self.animationPositionArray[self.positionIndex][1])
|
||||
self.positionIndex += 1
|
||||
self._indexOutEnd()
|
||||
|
||||
# 计算位置
|
||||
if self.animationPositionArray:
|
||||
keyframePosTime = float(self.animationPositionArray[self.positionIndex][0]) - self.preAnimationPosArgs[
|
||||
0] # 位置关键帧下个关键帧与上个关键帧之间的时间
|
||||
# 只有时间间隔大于 0,才计算当前这一刻的值
|
||||
if keyframePosTime > 0.0:
|
||||
positionX = 0
|
||||
positionY = 0
|
||||
positionZ = 0
|
||||
if self.animationPositionArray[self.positionIndex][2] == 'catmullrom':
|
||||
positionX = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 0)
|
||||
positionY = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 1)
|
||||
positionZ = getCatmullromLerpFromKeyframe(self.animationPositionArray, self.positionIndex,
|
||||
startToNowTime, 2)
|
||||
else:
|
||||
positionX = ((float(self.animationPositionArray[self.positionIndex][1][0]) -
|
||||
self.preAnimationPosArgs[1][0]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][0]
|
||||
positionY = ((float(self.animationPositionArray[self.positionIndex][1][1]) -
|
||||
self.preAnimationPosArgs[1][1]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][1]
|
||||
positionZ = ((float(self.animationPositionArray[self.positionIndex][1][2]) -
|
||||
self.preAnimationPosArgs[1][2]) / keyframePosTime * preToNowTime) + \
|
||||
self.preAnimationPosArgs[1][2]
|
||||
pos = (
|
||||
(1 if self.playConfig.isFirstPerson else -1) * positionX / 16.0,
|
||||
positionY / 16.0 - 1.62,
|
||||
(-1 if self.playConfig.isFirstPerson else 1) * positionZ / 16.0
|
||||
)
|
||||
posMat = Matrix.Create([
|
||||
[pos[0]],
|
||||
[pos[1]],
|
||||
[pos[2]]
|
||||
])
|
||||
if self.playConfig.isFirstPerson:
|
||||
# 第一人称
|
||||
rotMax = Matrix.CreateEye(3)
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
deltX = (nowCameraRotation[0] - self.preAnimationRotArgs[1][0])
|
||||
deltY = (nowCameraRotation[1] - self.preAnimationRotArgs[1][1])
|
||||
# 注意矩阵相乘顺序
|
||||
if self.playConfig.isPosFollowXRot:
|
||||
rotMax = self._leftRotationMatrixX(deltX)
|
||||
if self.playConfig.isPosFollowYRot:
|
||||
rotMax = self._leftRotationMatrixY(deltY - 180) * rotMax
|
||||
posMat = rotMax * posMat
|
||||
else:
|
||||
# 第三人称位置只受初始绕 y 轴旋转的影响
|
||||
posMat = self.leftRotYMat * posMat
|
||||
pos = (
|
||||
posMat[0, 0] + self.positionOrigin[0],
|
||||
posMat[1, 0] + self.positionOrigin[1],
|
||||
posMat[2, 0] + self.positionOrigin[2]
|
||||
)
|
||||
# 是否不设置
|
||||
isNotSet = False
|
||||
if callable(self.playConfig.tickPosFunc):
|
||||
isNotSet = self.playConfig.tickPosFunc(startToNowTime, pos)
|
||||
if not isNotSet:
|
||||
self.cameraCompFactory.SetCameraAnchor(pos)
|
||||
self.preAnimationPosArgs = (startToNowTime, [positionX, positionY, positionZ])
|
||||
|
||||
# 计算旋转
|
||||
if self.animationRotationArray:
|
||||
keyframeRotTime = float(self.animationRotationArray[self.rotationIndex][0]) - self.preAnimationRotArgs[
|
||||
0] # 旋转关键帧下个关键帧与上个关键帧之间的时间
|
||||
# 只有时间间隔大于 0,才计算当前这一刻的值
|
||||
if keyframeRotTime > 0.0:
|
||||
rotationX = 0
|
||||
rotationY = 0
|
||||
rotationZ = 0
|
||||
if self.animationRotationArray[self.rotationIndex][2] == 'catmullrom':
|
||||
rotationX = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 0)
|
||||
rotationY = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 1)
|
||||
rotationZ = getCatmullromLerpFromKeyframe(self.animationRotationArray, self.rotationIndex,
|
||||
startToNowTime, 2)
|
||||
else:
|
||||
rotationX = ((float(self.animationRotationArray[self.rotationIndex][1][0]) -
|
||||
self.preAnimationRotArgs[1][0]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][0]
|
||||
rotationY = ((float(self.animationRotationArray[self.rotationIndex][1][1]) -
|
||||
self.preAnimationRotArgs[1][1]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][1]
|
||||
rotationZ = ((float(self.animationRotationArray[self.rotationIndex][1][2]) -
|
||||
self.preAnimationRotArgs[1][2]) / keyframeRotTime * preToNowTime) + \
|
||||
self.preAnimationRotArgs[1][2]
|
||||
deltX = self.rotationOrigin[0] if self.playConfig.isFirstPerson else 0
|
||||
deltY = self.rotationOrigin[1] - (180 if self.playConfig.isFirstPerson else 0)
|
||||
if self.playConfig.isAllowDrag:
|
||||
nowCameraRotation = self.cameraCompFactory.GetCameraRotation()
|
||||
deltX = (nowCameraRotation[0] - self.preAnimationRotArgs[1][0])
|
||||
deltY = (nowCameraRotation[1] - self.preAnimationRotArgs[1][1])
|
||||
deltX += rotationX
|
||||
deltY += rotationY
|
||||
rot = (
|
||||
deltX % (-360 if deltX < 0 else 360),
|
||||
deltY % (-360 if deltY < 0 else 360),
|
||||
# 军械库版本,因为摄像机动画直接使用 Java 版动画,所以 y 轴没有旋转 180 度,因此 z 轴旋转从 - 改为 +,以匹配 Blockbench 里的效果
|
||||
self.rotationOrigin[2] + rotationZ
|
||||
)
|
||||
# 是否不设置
|
||||
isNotSet = False
|
||||
if callable(self.playConfig.tickRotFunc):
|
||||
isNotSet = self.playConfig.tickRotFunc(startToNowTime, rot)
|
||||
if not isNotSet:
|
||||
self.cameraCompFactory.SetCameraRotation(rot)
|
||||
self.beforeRot = rot
|
||||
self.preAnimationRotArgs = (
|
||||
startToNowTime, [rotationX, rotationY, rotationZ]) # 更新上一帧的参数为当前参数,为下一次计算做准备
|
||||
|
||||
def HealthChangeClientEvent(self, event):
|
||||
"""
|
||||
生物生命值发生变化时触发
|
||||
"""
|
||||
# 自己死亡时,停止当前摄像机动画
|
||||
playerType = clientApi.GetMinecraftEnum().EntityType.Player
|
||||
if event['entityId'] == clientApi.GetLocalPlayerId() and compFactory.CreateEngineType(
|
||||
event['entityId']).GetEngineType() & playerType == playerType and event['to'] <= 0.0:
|
||||
self.stop(True)
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
from .Camera import Camera, compFactory
|
||||
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
class CameraListen(object):
|
||||
"""
|
||||
摄像机监听类,创建此类的对象,以手动控制摄像机的监听与取消监听
|
||||
"""
|
||||
|
||||
cameraListenObj = None # type: CameraListen
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GameRenderTickEvent(self, event):
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.GameRenderTickEvent(event)
|
||||
|
||||
def HealthChangeClientEvent(self, event):
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.HealthChangeClientEvent(event)
|
||||
|
||||
|
||||
# 单个实例
|
||||
CameraListen.cameraListenObj = CameraListen()
|
||||
|
||||
# 取消监听定时器
|
||||
_timer = None
|
||||
|
||||
# 是否开启了内置监听
|
||||
_enable = False
|
||||
|
||||
|
||||
def startListen(client):
|
||||
"""
|
||||
开始监听
|
||||
"""
|
||||
global _enable
|
||||
cameraListenObj = CameraListen.cameraListenObj
|
||||
|
||||
# 销毁自身已有监听
|
||||
stopListen(client)
|
||||
|
||||
# 销毁摄像机对象内置监听
|
||||
if Camera.globalCameraObj:
|
||||
Camera.globalCameraObj.destroyListen()
|
||||
|
||||
_enable = True
|
||||
client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent", cameraListenObj, cameraListenObj.GameRenderTickEvent, 10)
|
||||
client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "HealthChangeClientEvent", cameraListenObj, cameraListenObj.HealthChangeClientEvent)
|
||||
|
||||
|
||||
def stopListen(client, delayTime=0):
|
||||
"""
|
||||
取消监听
|
||||
"""
|
||||
global _timer
|
||||
cameraListenObj = CameraListen.cameraListenObj
|
||||
comp = compFactory.CreateGame(clientApi.GetLevelId())
|
||||
|
||||
def _scheduleCancel():
|
||||
global _timer, _enable
|
||||
|
||||
# 如果取消监听时,发现还存在未停止的摄像机动画,不取消监听,再次创建一个定时器
|
||||
if Camera.globalCameraObj and delayTime > 0:
|
||||
if _timer:
|
||||
_timer = comp.CancelTimer(_timer)
|
||||
_timer = comp.AddTimer(delayTime, _scheduleCancel)
|
||||
return
|
||||
|
||||
_enable = False
|
||||
client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent", cameraListenObj, cameraListenObj.GameRenderTickEvent)
|
||||
client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "HealthChangeClientEvent", cameraListenObj,
|
||||
cameraListenObj.HealthChangeClientEvent)
|
||||
|
||||
if delayTime > 0:
|
||||
_timer = comp.AddTimer(delayTime, _scheduleCancel)
|
||||
else:
|
||||
if _timer:
|
||||
_timer = comp.CancelTimer(_timer)
|
||||
_scheduleCancel()
|
||||
|
||||
|
||||
def stopListenKeepLastAnim(client):
|
||||
"""
|
||||
取消监听,并且自动保留取消监听前最后一个摄像机动画的完整播放
|
||||
"""
|
||||
if Camera.globalCameraObj and Camera.globalCameraObj.animationLength is not None and Camera.globalCameraObj.startAnimationTime is not None:
|
||||
t = Camera.globalCameraObj.animationLength - (time.time() - Camera.globalCameraObj.startAnimationTime)
|
||||
stopListen(client, t)
|
||||
else:
|
||||
stopListen(client)
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
import time
|
||||
import mod.client.extraClientApi as clientApi
|
||||
|
||||
from .utils.CatmullRom import getCatmullromLerpFromKeyframe
|
||||
from .Camera import Camera, levelId, compFactory
|
||||
from .exception.NotClientSystemException import NotClientSystemException
|
||||
|
||||
class Interpolation(object):
|
||||
|
||||
# 全局对象
|
||||
globalObj = None
|
||||
|
||||
def __init__(self, client, keyframes, isReturn=False):
|
||||
if not isinstance(client, clientApi.GetClientSystemCls()):
|
||||
raise NotClientSystemException("传入 Interpolation 内的 client 不是一个 ClientSystem")
|
||||
|
||||
self.client = client
|
||||
# 开始时间
|
||||
self.startTime = time.time()
|
||||
# 更新时间(再次调用 setInterpolation 时的时间)
|
||||
self.updateTime = self.startTime
|
||||
# 关键帧数组
|
||||
self.keyframes = keyframes
|
||||
# 关键帧数组索引
|
||||
self.index = 0
|
||||
# 前一帧索引,用于关键帧循环
|
||||
self.preIndex = 0
|
||||
# 前一帧计算出的旋转插值
|
||||
self.preRot = (0, 0)
|
||||
# 用于回正插值的缓存
|
||||
self.tempRot = [0, 0, 0]
|
||||
# 当前是否是回正插值
|
||||
self.isReturn = isReturn
|
||||
# 前一帧的摄像机角度
|
||||
self.beforeRot = None
|
||||
# 是否取消回正插值
|
||||
self.cancelReturn = False
|
||||
# 摄像机接口对象
|
||||
self.cameraComponentFactory = compFactory.CreateCamera(levelId)
|
||||
|
||||
self.client.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent", self, self.onGameRenderTick, 0)
|
||||
|
||||
def onGameRenderTick(self, event):
|
||||
"""
|
||||
每 Tick 触发
|
||||
"""
|
||||
nowTime = time.time()
|
||||
# 回正超时后自动停止
|
||||
if self.isReturn and nowTime - self.startTime > self.keyframes[-1][0]:
|
||||
self.stop()
|
||||
|
||||
# 当发现关键帧循环时,清空特定变量
|
||||
if self.index < self.preIndex:
|
||||
self.preRot = (0, 0)
|
||||
|
||||
startToNowTime = min(
|
||||
nowTime - self.updateTime + self.keyframes[min(max(self.index - 1, 0), len(self.keyframes) - 1)][0],
|
||||
self.keyframes[self.index][0]
|
||||
)
|
||||
rotX = getCatmullromLerpFromKeyframe(self.keyframes, self.index, startToNowTime, 0)
|
||||
rotY = getCatmullromLerpFromKeyframe(self.keyframes, self.index, startToNowTime, 1)
|
||||
nowRot = self.cameraComponentFactory.GetCameraRotation()
|
||||
|
||||
# 比较摄像机前后帧是否有较大的偏差,如果有,认为是玩家拖动屏幕造成的,取消回正
|
||||
if self.beforeRot:
|
||||
tolerance = 0.3
|
||||
# y 轴角度是 -180 到 180 的循环,所以在计算角度差时要特殊处理
|
||||
y = abs(nowRot[1] - self.beforeRot[1])
|
||||
if abs(nowRot[0] - self.beforeRot[0]) > tolerance or min(y, 360 - y) > tolerance:
|
||||
self.cancelReturn = True
|
||||
|
||||
# 混合之前的事件中设置的摄像机旋转
|
||||
beforeEventRot = (0, 0, 0)
|
||||
if Camera.globalCameraObj and Camera.globalCameraObj.beforeRot:
|
||||
beforeEventRot = Camera.globalCameraObj.beforeRot
|
||||
|
||||
tempRot = (
|
||||
rotX - self.preRot[0],
|
||||
rotY - self.preRot[1],
|
||||
0
|
||||
)
|
||||
deltX = nowRot[0] + tempRot[0]
|
||||
deltY = nowRot[1] + tempRot[1]
|
||||
rot = (
|
||||
deltX % (-360 if deltX < 0 else 360),
|
||||
deltY % (-360 if deltY < 0 else 360),
|
||||
tempRot[2] + beforeEventRot[2]
|
||||
)
|
||||
self.cameraComponentFactory.SetCameraRotation(rot)
|
||||
self.beforeRot = rot
|
||||
self.preRot = (rotX, rotY)
|
||||
self.preIndex = self.index
|
||||
self.tempRot[0] -= tempRot[0]
|
||||
self.tempRot[1] -= tempRot[1]
|
||||
self.tempRot[2] -= tempRot[2]
|
||||
|
||||
def stop(self):
|
||||
self.client.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "GameRenderTickEvent", self, self.onGameRenderTick)
|
||||
Interpolation.globalObj = None
|
||||
if not self.isReturn and not self.cancelReturn:
|
||||
# 启动回正
|
||||
endTime = math.sqrt(self.tempRot[0] * self.tempRot[0] + self.tempRot[1] * self.tempRot[1] + self.tempRot[2] * self.tempRot[2])
|
||||
endTime *= 0.3
|
||||
keyframes = [
|
||||
(0.0, [0, 0, 0]),
|
||||
(endTime, self.tempRot)
|
||||
]
|
||||
setInterpolation(self.client, keyframes, 1, isReturn=True)
|
||||
|
||||
def setInterpolation(client, keyframes, index, speed=10.0, isReturn=False):
|
||||
"""
|
||||
设置插值
|
||||
|
||||
Args:
|
||||
client (ClientSystem): 客户端对象
|
||||
keyframes (list): 关键帧数组,格式:[(0.0, [1.0, 2.5, 3.3]),(1.0, [-3.0, 2.5, 3.3])]
|
||||
index (int): 关键帧数组索引
|
||||
speed (float, optional): 速度。默认值 10.0
|
||||
isReturn (bool, optional): 是否是回正插值。默认值 False
|
||||
"""
|
||||
if not Interpolation.globalObj:
|
||||
keyframes = [(t / speed, i) for t, i in keyframes]
|
||||
Interpolation.globalObj = Interpolation(client, keyframes, isReturn)
|
||||
elif Interpolation.globalObj.isReturn:
|
||||
# 正在回正中,强制停止
|
||||
Interpolation.globalObj.stop()
|
||||
keyframes = [(t / speed, i) for t, i in keyframes]
|
||||
Interpolation.globalObj = Interpolation(client, keyframes, isReturn)
|
||||
Interpolation.globalObj.updateTime = time.time()
|
||||
Interpolation.globalObj.index = index
|
||||
|
||||
def stopInterpolation():
|
||||
"""
|
||||
停止插值
|
||||
"""
|
||||
if Interpolation.globalObj:
|
||||
Interpolation.globalObj.stop()
|
||||
Whitespace-only changes.
+34
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class PlayConfig(object):
|
||||
|
||||
def __init__(self, isFirstPerson = False, isAllowDrag = False, isAllowMove = False, isPosFollowXRot = True, isPosFollowYRot = True, isZRotForceReturn = True, isZRotStartOrigin = False, isDelayOneFrame = True, isLockPerspective = True, tickPosFunc = None, tickRotFunc = None, playAfterFunc = None):
|
||||
"""
|
||||
play 方法的参数对象
|
||||
|
||||
Args:
|
||||
isFirstPerson (bool, optional): 是否是第一人称动画。如果是,那么 Y 轴旋转会减 180 度。默认值 False
|
||||
isAllowDrag (bool, optional): 是否允许玩家拖拽视角。默认值 False
|
||||
isAllowMove (bool, optional): 是否允许玩家移动。默认值 False
|
||||
isPosFollowXRot (bool, optional): 摄像机空间位置坐标是否受 X 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isPosFollowYRot (bool, optional): 摄像机空间位置坐标是否受 Y 轴旋转影响(仅在第一人称下生效)。默认值 True
|
||||
isZRotForceReturn (bool, optional): 是否在动画播放完成后强制归零 Z 轴的旋转值。默认值 True
|
||||
isZRotStartOrigin (bool, optional): 是否将动画播放前的 Z 轴旋转值作为初始的 Z 轴旋转值。置为 False 可防止玩家在受伤瞬间播放摄像机动画造成的 Z 轴旋转异常(json 配置 override_previous_animation 值为 True 时无效)。默认值 False
|
||||
isDelayOneFrame (bool, optional): 是否延迟一帧再播放摄像机动画。默认值 True
|
||||
isLockPerspective (bool, optional): 是否锁定并强制切换玩家视角。默认值 True
|
||||
tickPosFunc (function, optional): 摄像机动画播放时,在每次设置摄像机位置前执行的函数。默认值 None
|
||||
tickRotFunc (function, optional): 摄像机动画播放时,在每次设置摄像机旋转前执行的函数。默认值 None
|
||||
playAfterFunc (function, optional): 摄像机动画播放结束后的回调函数。默认值 None
|
||||
"""
|
||||
self.isFirstPerson = isFirstPerson
|
||||
self.isAllowDrag = isAllowDrag
|
||||
self.isAllowMove = isAllowMove
|
||||
self.isPosFollowXRot = isPosFollowXRot
|
||||
self.isPosFollowYRot = isPosFollowYRot
|
||||
self.isZRotForceReturn = isZRotForceReturn
|
||||
self.isZRotStartOrigin = isZRotStartOrigin
|
||||
self.isDelayOneFrame = isDelayOneFrame
|
||||
self.isLockPerspective = isLockPerspective
|
||||
self.tickPosFunc = tickPosFunc
|
||||
self.tickRotFunc = tickRotFunc
|
||||
self.playAfterFunc = playAfterFunc
|
||||
Whitespace-only changes.
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class NotClientSystemException(Exception):
|
||||
"""
|
||||
不是客户端系统异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(NotClientSystemException, self).__init__(errorMessage)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class NotPlayConfigException(Exception):
|
||||
"""
|
||||
不是播放配置对象异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(NotPlayConfigException, self).__init__(errorMessage)
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class SafeEvalException(Exception):
|
||||
"""
|
||||
安全计算异常
|
||||
"""
|
||||
|
||||
def __init__(self, errorMessage):
|
||||
super(SafeEvalException, self).__init__(errorMessage)
|
||||
Whitespace-only changes.
+108
@@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
class SplineCurve:
|
||||
"""
|
||||
Catmull-Rom 样条曲线类
|
||||
"""
|
||||
|
||||
def __init__(self, points):
|
||||
"""
|
||||
初始化样条曲线
|
||||
points: 值列表
|
||||
"""
|
||||
self.points = points
|
||||
|
||||
def getPoint(self, t):
|
||||
"""根据参数 t 获取曲线上的点"""
|
||||
if len(self.points) < 2:
|
||||
return 0
|
||||
|
||||
# 处理边界情况
|
||||
if t <= 0:
|
||||
return self.points[0]
|
||||
if t >= 1:
|
||||
return self.points[-1]
|
||||
|
||||
# 找到包含 t 的线段
|
||||
numSegments = len(self.points) - 1
|
||||
segmentIndex = int(t * numSegments)
|
||||
segmentIndex = max(0, min(segmentIndex, numSegments - 1))
|
||||
|
||||
# 计算线段内的局部参数
|
||||
localT = t * numSegments - segmentIndex
|
||||
|
||||
# 获取四个控制点用于 Catmull-Rom 插值
|
||||
p0Index = max(0, segmentIndex - 1)
|
||||
p1Index = segmentIndex
|
||||
p2Index = segmentIndex + 1
|
||||
p3Index = min(len(self.points) - 1, segmentIndex + 2)
|
||||
|
||||
p0 = self.points[p0Index]
|
||||
p1 = self.points[p1Index]
|
||||
p2 = self.points[p2Index]
|
||||
p3 = self.points[p3Index]
|
||||
|
||||
# Catmull-Rom 插值公式
|
||||
t2 = localT * localT
|
||||
t3 = t2 * localT
|
||||
|
||||
# 计算插值点的 y 坐标
|
||||
y = 0.5 * (
|
||||
(2 * p1) +
|
||||
(-p0 + p2) * localT +
|
||||
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
|
||||
(-p0 + 3 * p1 - 3 * p2 + p3) * t3
|
||||
)
|
||||
return y
|
||||
|
||||
def getCatmullromLerp(beforePlus, before, after, afterPlus, axis, alpha):
|
||||
"""
|
||||
Catmull-Rom 插值函数
|
||||
|
||||
Args:
|
||||
beforePlus: 前一个关键帧的前一个关键帧
|
||||
before: 前一个关键帧
|
||||
after: 后一个关键帧
|
||||
afterPlus: 后一个关键帧的后一个关键帧
|
||||
axis: 坐标轴 (x: 0, y: 1, z: 2)
|
||||
alpha: 插值参数 (0-1)
|
||||
|
||||
Returns:
|
||||
插值结果
|
||||
"""
|
||||
vectors = []
|
||||
|
||||
# 构建控制点向量
|
||||
if beforePlus:
|
||||
vectors.append(beforePlus[1][axis])
|
||||
if before:
|
||||
vectors.append(before[1][axis])
|
||||
if after:
|
||||
vectors.append(after[1][axis])
|
||||
if afterPlus:
|
||||
vectors.append(afterPlus[1][axis])
|
||||
|
||||
# 创建样条曲线
|
||||
curve = SplineCurve(vectors)
|
||||
|
||||
# 计算时间参数
|
||||
timeParam = (alpha + (1 if beforePlus else 0)) / (len(vectors) - 1) if len(vectors) > 1 else 0
|
||||
|
||||
# 获取插值点并返回 y 坐标
|
||||
point = curve.getPoint(timeParam)
|
||||
return point
|
||||
|
||||
def getCatmullromLerpFromKeyframe(keyframeArray, index, nowTime, axis):
|
||||
"""
|
||||
通过关键帧数组获取当前时间点的平滑插值
|
||||
"""
|
||||
if not keyframeArray:
|
||||
return 0
|
||||
endIndex = len(keyframeArray) - 1
|
||||
kf0 = keyframeArray[max(index - 2, 0)]
|
||||
kf1 = keyframeArray[max(index - 1, 0)]
|
||||
kf2 = keyframeArray[min(index, endIndex)]
|
||||
kf3 = keyframeArray[min(index + 1, endIndex)]
|
||||
den = (kf2[0] - kf1[0])
|
||||
alpha = (nowTime - kf1[0]) / den if den != 0 else 0
|
||||
return getCatmullromLerp(kf0, kf1, kf2, kf3, axis, alpha)
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..exception.SafeEvalException import SafeEvalException
|
||||
|
||||
class SafeEval:
|
||||
def __init__(self):
|
||||
self.operators = {
|
||||
'+': lambda x, y: x + y,
|
||||
'-': lambda x, y: x - y,
|
||||
'*': lambda x, y: x * y,
|
||||
'/': lambda x, y: x / y if y != 0 else self.raiseException("发生了除以 0 错误")
|
||||
}
|
||||
|
||||
def tokenize(self, expression):
|
||||
"""将表达式分割成数字和运算符"""
|
||||
tokens = []
|
||||
number = ''
|
||||
negative = False # 用于标记负号
|
||||
for char in expression:
|
||||
if char.isspace():
|
||||
continue
|
||||
elif char.isdigit() or char == '.':
|
||||
number += char
|
||||
elif char == '-':
|
||||
if not number: # 如果前面没有数字,则表示负号
|
||||
negative = True
|
||||
else:
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
number = ''
|
||||
negative = False
|
||||
tokens.append(char)
|
||||
else:
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
number = ''
|
||||
negative = False
|
||||
tokens.append(char)
|
||||
if number:
|
||||
tokens.append(float(number) if not negative else -float(number))
|
||||
return tokens
|
||||
|
||||
def evaluate(self, expression):
|
||||
"""计算表达式的值"""
|
||||
tokens = self.tokenize(expression)
|
||||
return self.parse_expression(tokens)
|
||||
|
||||
def parse_expression(self, tokens):
|
||||
"""解析表达式"""
|
||||
return self.parse_additive(tokens)
|
||||
|
||||
def parse_additive(self, tokens):
|
||||
"""解析加法和减法"""
|
||||
result = self.parse_multiplicative(tokens)
|
||||
while tokens and tokens[0] in ('+', '-'):
|
||||
op = tokens.pop(0)
|
||||
if op == '+':
|
||||
right = self.parse_multiplicative(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
elif op == '-':
|
||||
right = self.parse_multiplicative(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
return result
|
||||
|
||||
def parse_multiplicative(self, tokens):
|
||||
"""解析乘法和除法"""
|
||||
result = self.parse_primary(tokens)
|
||||
while tokens and tokens[0] in ('*', '/'):
|
||||
op = tokens.pop(0)
|
||||
if op == '*':
|
||||
right = self.parse_primary(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
elif op == '/':
|
||||
right = self.parse_primary(tokens)
|
||||
result = self.operators[op](result, right)
|
||||
return result
|
||||
|
||||
def parse_primary(self, tokens):
|
||||
"""解析基本表达式"""
|
||||
if not tokens:
|
||||
return None
|
||||
token = tokens.pop(0)
|
||||
if isinstance(token, float):
|
||||
return token
|
||||
elif token == '(':
|
||||
result = self.parse_expression(tokens)
|
||||
if not tokens or tokens[0] != ')':
|
||||
return self.raiseException("括号不匹配")
|
||||
tokens.pop(0) # Pop the closing parenthesis
|
||||
return result
|
||||
else:
|
||||
return self.raiseException("无效的语法")
|
||||
|
||||
def raiseException(self, message):
|
||||
"""抛出异常"""
|
||||
SafeEvalException(message)
|
||||
Reference in new issue
Block a user