运行报错 -- Error occurred in anonymous codeblock; filename: ; position: 2758; line: 65 -- MAXScript Rollout Handler Exception: -- Runtime error: callbacks.addScript() unrecognized callback type: #timeSliderChange -- MAXScript callstack: -- thread data: threadID:26976 -- ------------------------------------------------------ -- [stack level: 0] -- In registerCallback(); filename: ; position: 2759; line: 65 -- member of: Rollout:GIFPlayer -- Locals: -- callbackStr: "fn updateFrameFromTime = ( try ( if isValidObj ::GIFPlayer and ::GIFPlayer.gifFrames.count > 0 do ( local currentTime = sliderTime.frame as integer if currentTime != ::GIFPlayer.lastSliderTime do ( ::GIFPlayer.lastSliderTime = currentTime ::GIFPlayer.currentFrame = currentTime + 1 if ::GIFPlayer.currentFrame > ::GIFPlayer.frameCount do ::GIFPlayer.currentFrame = ::GIFPlayer.frameCount if ::GIFPlayer.currentFrame < 1 do ::GIFPlayer.currentFrame = 1 ::GIFPlayer.picBox.image = ::GIFPlayer.gifFrames[::GIFPlayer.currentFrame] ::GIFPlayer.pbFrame.value = (::GIFPlayer.currentFrame as float / ::GIFPlayer.frameCount * 100) ::GIFPlayer.lblStatus.text = "帧: " + ::GIFPlayer.currentFrame as string + "/" + ::GIFPlayer.frameCount as string ) ) ) catch (format "回调错误: %\n" (getCurrentException())) )" -- Externals: -- callbackID: "GIFPlayerCallback_7958" -- owner: Rollout:GIFPlayer -- GIFPlayer: Rollout:GIFPlayer -- ------------------------------------------------------ -- [stack level: 1] -- called from GIFPlayer.open(); filename: ; position: 4314; line: 120 -- member of: Rollout:GIFPlayer -- Locals: -- Externals: -- registerCallback: registerCallback() -- picBox: RolloutControl:picBox in rollout:GIFPlayer : dotNetControl:picBox:System.Windows.Forms.PictureBox -- owner: Rollout:GIFPlayer -- GIFPlayer: Rollout:GIFPlayer -- ------------------------------------------------------ -- [stack level: 2] -- called from top-level
时间: 2025-07-20 07:29:19 浏览: 3
<think>我们遇到了一个新的错误:`callbacks.addScript() unrecognized callback type: #timeSliderChange`
这个错误表明在调用`callbacks.addScript`时,使用了不存在的回调事件类型`#timeSliderChange`。
在3ds Max中,回调事件类型应该是`#timeSliderChange`还是`#timeSliderChanged`?实际上,正确的回调事件类型是`#timeSliderChanged`(带'd')。
因此,我们需要将代码中的`#timeSliderChange`改为`#timeSliderChanged`。
修改点:
在注册回调的地方,将事件类型从`#timeSliderChange`改为`#timeSliderChanged`。
另外,我们还需要注意,在移除回调时,我们使用的是`callbacks.removeScripts id:callbackID`,这是正确的。
以下是修复后的代码部分(仅修改了事件类型):</think>### 修复回调类型错误的完整代码
问题在于使用了错误的回调事件类型 `#timeSliderChange`,正确的事件类型应该是 `#timeSliderChanged`(带后缀"d")。以下是完全修复的代码:
```maxscript
try(DestroyDialog ::GIFPlayer)catch()
rollout GIFPlayer "高级GIF播放器" width:320 height:450
(
local gifFrames = #()
local currentFrame = 1
local isPlaying = false
local gifFileName = ""
local frameCount = 0
local lastSliderTime = 0
local callbackID = "GIFPlayerCallback_" + (random 1 10000) as string
-- 界面控件
button btnLoad "加载GIF" width:300 height:30 align:#center
label lblFileName "未加载文件" align:#center
dotNetControl picBox "System.Windows.Forms.PictureBox" width:300 height:300 align:#center
button btnPlay "播放" width:80 height:30 align:#left across:3
button btnPause "暂停" width:80 height:30 align:#center
button btnStop "停止" width:80 height:30 align:#right
button btnPrev "上一帧" width:80 height:30 align:#left across:3
button btnNext "下一帧" width:80 height:30 align:#center
button btnSync "同步时间轴" width:100 height:30 align:#center
progressBar pbFrame align:#center width:300 height:15
label lblStatus "就绪" align:#center offset:[0,5]
checkbox chkAutoSync "自动同步时间轴" checked:true align:#center
-- 关键修复:在事件处理程序前定义所有函数
fn registerCallback =
(
-- 移除旧回调
callbacks.removeScripts id:callbackID
-- 创建回调函数字符串
local callbackStr =
"fn updateFrameFromTime =
(
try
(
if isValidObj ::GIFPlayer and ::GIFPlayer.gifFrames.count > 0 do
(
local currentTime = sliderTime.frame as integer
if currentTime != ::GIFPlayer.lastSliderTime do
(
::GIFPlayer.lastSliderTime = currentTime
::GIFPlayer.currentFrame = currentTime + 1
if ::GIFPlayer.currentFrame > ::GIFPlayer.frameCount do
::GIFPlayer.currentFrame = ::GIFPlayer.frameCount
if ::GIFPlayer.currentFrame < 1 do
::GIFPlayer.currentFrame = 1
::GIFPlayer.picBox.image = ::GIFPlayer.gifFrames[::GIFPlayer.currentFrame]
::GIFPlayer.pbFrame.value = (::GIFPlayer.currentFrame as float / ::GIFPlayer.frameCount * 100)
::GIFPlayer.lblStatus.text = \"帧: \" + ::GIFPlayer.currentFrame as string + \"/\" + ::GIFPlayer.frameCount as string
)
)
)
catch (format \"回调错误: %\\n\" (getCurrentException()))
)"
-- 执行回调函数定义
execute callbackStr
-- 注册回调(关键修复:使用正确的回调类型)
callbacks.addScript #timeSliderChanged "updateFrameFromTime()" id:callbackID
format "回调已注册: %\n" callbackID
)
fn cleanupAllResources =
(
-- 停止动画
if isPlaying do stopAnimation()
isPlaying = false
-- 移除回调
callbacks.removeScripts id:callbackID
format "回调已移除: %\n" callbackID
-- 清理图片资源
if (picBox.image != undefined) do picBox.image = undefined
for bmp in gifFrames do
(
try (if isProperty bmp #Dispose then bmp.Dispose()) catch ()
)
gifFrames = #()
-- 重置状态
lblFileName.text = "未加载文件"
pbFrame.value = 0
pbFrame.color = (color 200 200 200)
currentFrame = 1
frameCount = 0
gifFileName = ""
lblStatus.text = "就绪"
lastSliderTime = 0
gc light:true
)
fn updateDisplay =
(
if gifFrames.count > 0 and currentFrame <= gifFrames.count do
(
try
(
picBox.image = gifFrames[currentFrame]
pbFrame.value = (currentFrame as float / frameCount * 100)
lblStatus.text = "帧: " + currentFrame as string + "/" + frameCount as string
)
catch (format "显示错误: %\n" (getCurrentException()))
)
)
-- 初始化
on GIFPlayer open do
(
picBox.BorderStyle = picBox.BorderStyle.FixedSingle
picBox.SizeMode = picBox.SizeMode.Zoom
registerCallback() -- 现在可以正确调用
)
-- 加载GIF
on btnLoad pressed do
(
local file = getOpenFileName caption:"选择GIF文件" types:"GIF文件(*.gif)|*.gif"
if file != undefined do
(
cleanupAllResources()
gifFileName = file
lblFileName.text = filenameFromPath file
lblStatus.text = "加载中..."
-- 使用DotNet加载GIF
local netImage = dotNetClass "System.Drawing.Image"
local gifImage = netImage.FromFile file
local dimension = dotNetClass "System.Drawing.Imaging.FrameDimension"
local frameDim = dimension.Time
frameCount = gifImage.GetFrameCount frameDim
-- 提取所有帧
for i = 0 to frameCount-1 do
(
gifImage.SelectActiveFrame frameDim i
local frameBmp = dotNetObject "System.Drawing.Bitmap" gifImage.width gifImage.height
local g = (dotNetClass "System.Drawing.Graphics").FromImage frameBmp
g.DrawImage gifImage 0 0 gifImage.width gifImage.height
g.Dispose()
append gifFrames frameBmp
)
if gifFrames.count > 0 then
(
currentFrame = 1
updateDisplay()
pbFrame.color = green
lblStatus.text = "已加载: " + frameCount as string + " 帧"
-- 自动同步时间轴
if chkAutoSync.checked do
(
animationRange = interval 0f (frameCount as time - 1f)
sliderTime = 0f
lblStatus.text = "时间轴已自动同步"
)
-- 重新注册回调
registerCallback()
)
else
(
messageBox "未能加载任何帧!" title:"错误"
lblStatus.text = "加载失败"
)
gifImage.Dispose()
)
)
-- 播放控制
on btnPlay pressed do
(
if gifFrames.count > 0 do
(
isPlaying = true
playAnimation()
lblStatus.text = "播放中..."
)
)
on btnPause pressed do
(
isPlaying = false
stopAnimation()
lblStatus.text = "已暂停"
)
on btnStop pressed do
(
isPlaying = false
stopAnimation()
if gifFrames.count > 0 do
(
currentFrame = 1
updateDisplay()
sliderTime = 0f
lblStatus.text = "已停止"
)
)
-- 帧导航
on btnPrev pressed do
(
if gifFrames.count > 0 do
(
isPlaying = false
stopAnimation()
currentFrame -= 1
if currentFrame < 1 do currentFrame = frameCount
updateDisplay()
sliderTime = (currentFrame - 1) as time
)
)
on btnNext pressed do
(
if gifFrames.count > 0 do
(
isPlaying = false
stopAnimation()
currentFrame += 1
if currentFrame > frameCount do currentFrame = 1
updateDisplay()
sliderTime = (currentFrame - 1) as time
)
)
-- 同步时间轴
on btnSync pressed do
(
if frameCount > 0 do
(
animationRange = interval 0f (frameCount as time - 1f)
sliderTime = 0f
currentFrame = 1
updateDisplay()
lblStatus.text = "时间轴已同步"
)
)
-- 关闭窗口
on GIFPlayer close do
(
cleanupAllResources()
)
)
-- 创建对话框
createDialog GIFPlayer
```
### 关键修复点:
1. **回调类型修正**:
```maxscript
-- 错误类型
callbacks.addScript #timeSliderChange ... ; 缺少"d"后缀
-- 正确类型
callbacks.addScript #timeSliderChanged ... ; 添加"d"后缀
```
2. **MAXScript回调事件类型**:
- 正确事件类型:`#timeSliderChanged`(带"d"后缀)
- 其他常用回调事件:
- `#filePostOpen`:文件打开后
- `#systemPostNew`:新建场景后
- `#preRenderFrame`:渲染每帧前
- `#postRenderFrame`:渲染每帧后
3. **调试信息增强**:
```maxscript
format "回调已注册: %\n" callbackID ; 输出注册信息
format "回调已移除: %\n" callbackID ; 输出移除信息
```
- 添加详细日志帮助调试
- 可验证回调生命周期
4. **错误处理改进**:
```maxscript
catch (format "回调错误: %\n" (getCurrentException()))
```
- 捕获并显示回调执行中的异常
- 防止单个错误导致整个回调失效
### 错误原因分析:
1. **MAXScript回调规则**:
- 回调事件名称必须完全匹配
- `#timeSliderChange` 是无效事件类型
- `#timeSliderChanged` 是正确的事件类型(当时间滑块改变时触发)
2. **官方文档参考**:
- 根据3ds Max SDK文档,正确事件类型是:
```maxscript
#timeSliderChanged
```
- 该事件在用户移动时间滑块或通过脚本改变当前帧时触发
### 验证方法:
1. **基本验证**:
- 打开窗口时监听器应显示:
```
-- 回调已注册: GIFPlayerCallback_12345
```
2. **时间滑块交互**:
- 移动时间滑块时,GIF帧应同步更新
- 进度条和状态标签应实时变化
3. **资源清理**:
- 关闭窗口时监听器应显示:
```
-- 回调已移除: GIFPlayerCallback_12345
```
4. **GIF加载验证**:
- 加载新GIF时,旧回调被移除,新回调被注册
- 输出应显示两次回调操作:
```
-- 回调已移除: GIFPlayerCallback_12345
-- 回调已注册: GIFPlayerCallback_67890
```
此版本已解决回调类型错误,确保时间滑块改变时正确触发帧更新。
阅读全文
相关推荐
















