基础知识
本文介绍 Mini Studio 能实现的一些基础功能。读者需要对 Mini Studio 的有一个基础的了解,并能简单进行操作。
官方网站资料:
https://studio.mini1.cn/wiki/Tutorials/Introduce/Introduce.html
studio界面基础操作:
https://studio.mini1.cn/wiki/Tutorials/Introduce/BaseOperation/StudioMenu.html
关于项目建立和搭建的基础操作,请参考另外一个demo RPG游戏的文档:
RPG游戏demo设计文档
准备工作
打开studio登陆后,新建一个空地图(Empty Map)项目,工程名自定义即可。打开主界面后,首先在WorkSpace中放置一个大方块g0,作为地图的地基。
再分别放置方块g0_copy, g0_copy_2, g0_copy_3, g0_copy_4作为围墙:
所有的脚本范例,都将在这个范围内运行。
文档中一共包含了16个范例:
脚本范例分成客户端和服务器两类,为什么要这样来区分呢? 1 客户端以表现层为主: GUI界面,镜头控制,射线,声音等功能是客户端独有的 2 服务端以数据层为主: 运行在远程服务器,可以存储数据,防止很多作弊 3 一些特效只在客户端处理,性能更好 客户端侧范例 case3 -- 场景中增加一个NPC模型,播放动作,行走,需要读取animation,只能在客户端执行 case4 -- 测试GUI界面 建立一个按钮,按钮点击后,打开一个选择框 有界面操作,只能在客户端运行 case6 -- 玩家走近NPC后,NPC定住,弹出GUI界面 case7 -- 根据GUI按钮NPC选择不同逻辑 case10 -- 给玩家增加一个攻击按钮,点击的时候会向前方攻击 case14 -- 鼠标点击选中物体判断 pickup case15 -- 检查射线ray碰撞物体 case16 -- 播放声音特效 服务器侧范例 case1 -- 场景中增加方块,修改大小,位置,材质 case2 -- 设置一个触发器方块,碰撞后进行变色,位置修改 case5 -- 新建一个NPC,使用Act组件,NPC随机行走 case8 -- 生成一个建筑物,增加一个门,碰撞打开,自动关闭 case9 -- NPC判断与玩家的距离,靠近或者攻击 case11 -- 序列化一个table数据,并存在云端 case12 -- 序列化一个table数据,并存在云端(异步模式) case13 -- 建立一个立方体区域,判断物体进入
-- 在场景中增加一个方块,修改大小,位置,材质
function MTestcase.case1()
print( "MTestcase.case1" );
-- 增加一个cube立方体
local obj = SandboxNode.new('GeoSolid', game.WorkSpace )
obj.Name = 'cube_test1'
obj.GeoSolidShape = Enum.GeoSolidShape.Cuboid --Cuboid立方体 Cylinder圆柱体
obj.LocalPosition = Vector3.new( 100, 300, 100 ); --位置
obj.LocalScale = Vector3.new( 1, 2, 3 ); --缩放
obj.Euler = Vector3.new( 0, 45, 0 ); --45度
log( 'case1 ', obj.LocalEuler, obj.Euler )
if true then
--修改材质方法1 直接设置图片资源id
-- (需要先在 资源背包 - 云资源中上传一个图片,并右键获得资源id )
obj.TextureId = 'RainbowId&filetype=5://229435794039771136'
--RainbowId&filetype=5://239160774029742080 --另外一个材质
else
--修改材质方法2 新建一个材质,并设置该材质 (一般不这样使用)
local mat_green = Enum.MaterialTemplate.Material_green
obj.MaterialType = mat_green
end
--[[
-- 设置旋转,使变换的y轴沿着全局y轴,变换的z轴沿着全局z轴
-- 计算出一个四维向量Quaternion, 难度较大
local newq = Quaternion.New(0,0,2,1)
local dir = Vector3.New(0,0,1)
local up = Vector3.New(0,1,0)
local angle = 45; --角度
local rotation_, ret1, ret2 = newq:RotateAxisAngle(dir, up, angle); -- return Quaternion
log( 'case1 ', obj.Rotation, obj.LocalRotation, rotation_, ret1, ret2 )
--obj.LocalRotation = rotation_
]]
end
-- 设置一个触发器方块,碰撞后进行变色,位置修改
function MTestcase.case2()
print( "MTestcase.case2" );
-- 增加一个cube立方体
local obj = SandboxNode.new('GeoSolid', game.WorkSpace )
obj.Name = 'cube_test2'
obj.GeoSolidShape = Enum.GeoSolidShape.Cuboid --Cuboid立方体
obj.LocalPosition = Vector3.new( 100, 300, 100 ); --位置
obj.Color = ColorQuad.new( 255, 0, 0, 255 ); --红色
obj.Size = Vector3.new( 200, 200, 200 ); --碰撞盒子的大小
-- 增加碰撞回调函数
obj.Touched:connect( function(node, pos1, normal1 )
if node then
print("touched:", node , node.Name, pos1, normal1 )
-- 碰撞的时候随机改变颜色
obj.Color = ColorQuad.new( math.random(0,255), math.random(0,255), math.random(0,255), 255 );
-- 碰撞的时候按方向改变位置
local obj_pos = obj.LocalPosition;
obj.LocalPosition = Vector3.new( obj_pos.x + normal1.x*50, obj_pos.y, obj_pos.z + normal1.z*50 );
end
end)
end
-- 场景中增加一个NPC模型,播放动作,行走 (只能在客户端侧执行)
function MTestcase.case3()
print( "MTestcase.case3" );
-- 增加一个模型
local obj = SandboxNode.new('Model', game.WorkSpace )
obj.Name = 'model_test3'
obj.ModelId = 'sandboxSysId://entity/100028/body.omod' --增加一个野人
--obj.ModelId = 'sandboxSysId://entity/100113/body.omod' --增加一个野人战士
--obj.ModelId = 'sandboxSysId://entity/100114/body.omod' --增加一个野人伍长
--obj.ModelId = 'sandboxSysId://entity/100063/body.omod' --增加一个野人祭司 雷电祭司
--obj.ModelId = 'sandboxSysId://entity/100117/body.omod' --增加一个野人投矛手
obj.LocalPosition = Vector3.new( 200, 500, 200 ); --位置
obj.Anchored = false --位置不固定
--obj.Size = Vector3.new( 200, 200, 200 ); --碰撞盒子的大小
obj.Size = Vector3.new( 150, 160, 150 ) --碰撞盒子的大小
obj.Center = Vector3.new( 0, 80, 0 ) --盒子中心位置
wait(1)
local anim_seq = 0 --用来表示多个动作中的第N个
-- 增加碰撞回调函数
obj.Touched:connect( function(node, pos1, normal1 )
if node then
print("touched:", node , node.Name, pos1, normal1 )
--obj.Velocity = Vector3.new( normal1.x*50, 0, normal1.z*50 ); --行走效果
local obj_pos = obj.LocalPosition;
obj.LocalPosition = Vector3.new( obj_pos.x + normal1.x*10, obj_pos.y, obj_pos.z + normal1.z*10 );
-- 改动朝向,这里可以注释掉
--local face_position = obj.Position + normal1 * 100;
--LookAtTarget( obj, Vector3.new( face_position.x, 0, face_position.z ) )
local animationIDs = obj:GetAnimationIDs() -- 新动作管理类
print("animationIDs:", node , node.Name, animationIDs )
--print_table( animationIDs, 'GetAnimationIDs' )
-- 注意:
-- 只有客户端代码可以加载一个模型的动作,服务器不会去加载动作文件
-- 因此只有客户端可以拿到模型的所有动作列表
-- 但是在已知动作列表名字的情况下
-- 客户端和服务器都可以调用 PlayAnimation, 来播放和控制模型动作
local legacy_animation = obj:GetLegacyAnimation() -- 旧版动作管理类
if legacy_animation then
local anim_name_list = legacy_animation:GetAnimationIDs() -- 动作名字列表
print_table( animationIDs, 'Legacy' )
-- 所有动作列表
-- 1=100100(stand) 2=100130(下蹲) 3=100101(run) 4=100111 5=100105(attack) 6=100112 7=100109 8=100200 9=100113
-- 100124(jump die) 100114(eat) 100115(摇头) 100102(sleep) 100106(die) 100107(be_attacked)
anim_seq = anim_seq + 1 --每次播放不同的动作
if anim_seq > #anim_name_list then
anim_seq = 1
end
print( 'print animation', anim_seq, anim_name_list[ anim_seq ] )
obj:StopAllAnimation(false);
obj:PlayAnimation( '' .. anim_name_list[ anim_seq ], 1.0, 0 ); --播放动作
end
end
end)
end
-- 测试GUI界面 建立一个按钮,按钮点击后,打开一个选择框
function MTestcase.case4()
print( "MTestcase.case4" );
--创建一个新的ui容器 ui_root, 其他所有图片和按钮都放置在里面
local root = SandboxNode.New('UIRoot', game.WorkSpace)
root.Name = 'ui_root'
-- 建立一个风格一致的按钮
local function create_btn( root_parent )
local tmp_button = SandboxNode.New('UIButton', root_parent )
tmp_button.Icon = 'RainbowId&filetype=5://230837294905430016' --按钮背景
tmp_button.FillColor = ColorQuad.new( 0,0,0,0 )
tmp_button.TitleSize = 20
tmp_button.DownEffect=Enum.DownEffect.ColorEffect
return tmp_button
end
--在ui_root上创建按钮btn
local button = create_btn( game.WorkSpace.ui_root )
button.Title = "选择界面"
button.DownEffect = Enum.DownEffect.ScaledEffect
button.Size = Vector2.New(100, 50)
button.Position = Vector2.New(100, 100)
local callback_press_btn --点击按钮的回调函数
local select_ui --选择界面
button.Click:Connect(function()
print("点击打开选择界面")
callback_press_btn()
end)
--打开界面
callback_press_btn = function ()
if select_ui then
--界面已经建立,切换隐藏或者显示
if select_ui.Visible == true then
select_ui.Visible = false
else
select_ui.Visible = true
end
else
--界面不存在,建立新的
--背景图片
select_ui = SandboxNode.New('UIImage', game.WorkSpace.ui_root)
select_ui.Name = 'image'
select_ui.Visible = true
select_ui.Icon = 'RainbowId&filetype=5://230930788571418624' --背景
select_ui.Size = Vector2.New(500, 500)
select_ui.Position = Vector2.New(300, 100)
select_ui.Pivot = Vector2.New(0, 0) --背景起始点
select_ui.FillColor = ColorQuad.new( 255,255,255,128 )
--建立两个按钮
local button1 = create_btn( select_ui )
button1.Title = "选择1"
button1.Size = Vector2.New(300, 120)
button1.Position = Vector2.New(250, 100)
local button2 = create_btn( select_ui )
button2.Title = "选择2"
button2.Size = Vector2.New(300, 120)
button2.Position = Vector2.New(250, 300)
local bubble_id = -1 --聊天气泡id
button1.Click:Connect(function()
print("点击1")
local chat = game:GetService("Chat")
bubble_id = chat:ShowChatBubble( '点击 1', true, 1, Vector3.new(0, 500, 0), bubble_id )
select_ui.Visible = false
end)
button2.Click:Connect(function()
print("点击2")
local chat = game:GetService("Chat")
bubble_id = chat:ShowChatBubble( '点击 2', true, 1, Vector3.new(0, 500, 0), bubble_id )
select_ui.Visible = false
end)
end
end
end
-- 新建一个NPC,使用Act组件,NPC随机行走
function MTestcase.case5()
print( "MTestcase.case5" );
local data_store = {} --用来保存各种变量,传递参数给其他用例
-- 增加一个actor
local actor_yeren = SandboxNode.new('Actor', game.WorkSpace ) --actor会按照迷你游戏来表现行为和动画
actor_yeren.Name = 'model_yeren1'
actor_yeren.ModelId = 'sandboxSysId://entity/100063/body.omod' --增加一个野人祭祀
actor_yeren.LocalPosition = Vector3.new( 500, 300, 800 ); --位置
actor_yeren.Size = Vector3.new( 400, 200, 400 ) --碰撞盒子的大小
actor_yeren.Center = Vector3.new( 0, 100, 0 ) --盒子中心位置
data_store.actor_yeren = actor_yeren
wait(1)
local function cb_walk ()
local move_pos = Vector3.new( 5000 - math.random()*10000, 300, 5000 - math.random()*10000 ) --随机行动方向
print("NavigateTo:", move_pos )
actor_yeren:NavigateTo( move_pos ) --向随机的位置寻路行走
--actor_yeren:MoveTo( move_pos ) --向随机的位置直接行走
end
-- 一个定时器, 每N秒让 actor 行走
local timer = SandboxNode.New("Timer", game.WorkSpace)
timer.Delay = 1 -- 延迟多少秒开始
timer.Loop = true -- 是否循环
timer.Interval = 10 -- 循环间隔多少秒
timer.Callback = cb_walk
timer:Start() -- 启动定时器
print("timer start")
data_store.timer = timer
return data_store; -- 给其他用例使用
end
-- 玩家走近NPC后,NPC定住,弹出GUI界面
function MTestcase.case6()
print( "MTestcase.case6" );
local data_store_case5 = MTestcase.case5()
local actor_yeren = data_store_case5.actor_yeren
local timer = data_store_case5.timer
print("actor_yeren :", actor_yeren )
print("timer :", timer )
local gui_test -- 保存界面
--打开界面
local open_GUI = function ()
if gui_test then
--界面已经建立,切换隐藏或者显示
if gui_test.Visible == true then
gui_test.Visible = false
else
gui_test.Visible = true
end
else
print( 'open_GUI' )
--创建一个新的ui容器 ui_root, 其他所有图片和按钮都放置在里面
local root = SandboxNode.New('UIRoot', game.WorkSpace)
root.Name = 'ui_root'
--背景图片
gui_test = SandboxNode.New('UIImage', game.WorkSpace.ui_root)
gui_test.Name = 'image'
gui_test.Visible = true
gui_test.Icon = 'RainbowId&filetype=5://230930788571418624' --背景
gui_test.Size = Vector2.New(500, 400)
gui_test.Position = Vector2.New(300, 200)
gui_test.Pivot = Vector2.New(0, 0) --背景起始点
gui_test.FillColor = ColorQuad.new( 255,255,255,128 )
-- 建立一个风格一致的按钮
local function create_btn( root_parent )
local tmp_button = SandboxNode.New('UIButton', root_parent )
tmp_button.Icon = 'RainbowId&filetype=5://230837294905430016' --按钮背景
tmp_button.FillColor = ColorQuad.new( 0,0,0,0 )
tmp_button.TitleSize = 20
tmp_button.DownEffect=Enum.DownEffect.ColorEffect
return tmp_button
end
--建立按钮
local button1 = create_btn( gui_test )
button1.Icon = 'RainbowId&filetype=5://230837040806105088'
button1.Title = "野人: 你好~~"
button1.Size = Vector2.New(400, 120)
button1.Position = Vector2.New(250, 100)
local button2 = create_btn( gui_test )
button2.Title = "购买长矛"
button2.Size = Vector2.New(150, 90)
button2.Position = Vector2.New(120, 300)
button2.Click:Connect(function()
button1.Title = '你购买了' .. math.floor(math.random()*10) + 1 .. '个长矛~~';
end)
local button3 = create_btn( gui_test )
button3.Title = "拜 拜"
button3.Size = Vector2.New(150, 120)
button3.Position = Vector2.New(380, 300)
button3.Click:Connect(function()
gui_test.Visible = false
end)
end
end
-- 增加碰撞回调函数
actor_yeren.Touched:connect( function(node, pos1, normal1 )
if node then
print("touched :", node , node.Name )
if node.Name == 'player1' then
print("touched player1 :", node, node.Name )
actor_yeren:StopNavigate() -- 停止野人当前行走
if timer:GetRunState() == Enum.TimerRunState.RUNNING then
timer:Pause() --暂停定时器
end
open_GUI()
end
end
end )
end
-- 根据GUI按钮NPC选择不同逻辑
function MTestcase.case7()
print( "MTestcase.case7" );
-- 增加一个actor
local actor_yeren = SandboxNode.new('Actor', game.WorkSpace ) --actor会按照迷你游戏来表现行为和动画
actor_yeren.Name = 'model_yeren1'
actor_yeren.ModelId = 'sandboxSysId://entity/100028/body.omod' --增加一个野人
actor_yeren.LocalPosition = Vector3.new( 500, 300, 500 ); --位置
actor_yeren.Size = Vector3.new( 400, 200, 400 ); --碰撞盒子的大小
actor_yeren.Center = Vector3.new( 0, 100, 0 ) --盒子中心位置
wait(1)
local gui_test -- 保存界面
--打开界面
local open_GUI = function ()
if gui_test then
--界面已经建立,切换隐藏或者显示
if gui_test.Visible == false then
gui_test.Visible = true
end
else
print( 'open_GUI' )
--创建一个新的ui容器 ui_root, 其他所有图片和按钮都放置在里面
local root = SandboxNode.New('UIRoot', game.WorkSpace)
root.Name = 'ui_root'
--背景图片
gui_test = SandboxNode.New('UIImage', game.WorkSpace.ui_root)
gui_test.Name = 'image'
gui_test.Visible = true
gui_test.Icon = 'RainbowId&filetype=5://230930788571418624' --背景
gui_test.Size = Vector2.New(500, 400)
gui_test.Position = Vector2.New(300, 200)
gui_test.Pivot = Vector2.New(0, 0) --背景起始点
gui_test.FillColor = ColorQuad.new( 255,255,255,128 )
-- 建立一个风格一致的按钮
local function create_btn( root_parent )
local tmp_button = SandboxNode.New('UIButton', root_parent )
tmp_button.Icon = 'RainbowId&filetype=5://230837294905430016' --按钮背景
tmp_button.FillColor = ColorQuad.new( 0,0,0,0 )
tmp_button.TitleSize = 20
tmp_button.DownEffect=Enum.DownEffect.ColorEffect
return tmp_button
end
--建立按钮
local button1 = create_btn( gui_test )
button1.Icon = 'RainbowId&filetype=5://230837040806105088'
button1.Title = "野人: 你好~~"
button1.Size = Vector2.New(400, 120)
button1.Position = Vector2.New(250, 100)
local function attack( dir )
actor_yeren.LocalEuler = Vector3.new( 0, dir * -90, 0) --改变朝向
actor_yeren:PlayAnimation( '100105', 1.0, 1 ) --投掷动作
wait(0.5)
-- 可以这样新建一个长矛
local changmao = SandboxNode.new('Model', game.WorkSpace )
changmao.Name = 'model_changmao'
changmao.Anchored = false
changmao.ModelId = 'sandboxSysId://itemmods/12004/body.omod'
-- 或者先摆放一根长矛,再克隆出来
--local changmao = game.WorkSpace.changmao:Clone()
--changmao.Parent = game.WorkSpace;
changmao.LocalPosition = Vector3.new(actor_yeren.LocalPosition.x, actor_yeren.LocalPosition.y + 150, actor_yeren.LocalPosition.z)
changmao.Velocity = Vector3.new( 2000 * dir, 100, 0 )
--调整方向
changmao.Euler = Vector3.new( actor_yeren.Euler.x - 90 , actor_yeren.Euler.y, actor_yeren.Euler.z )
--print ( 'Euler: ', actor_yeren.Euler, ' ', changmao.Euler )
wait(10)
changmao:Destroy() --清理节点(节点会挂在game.WorldSpace下,destory后节约内存)
end
local button2 = create_btn( gui_test )
button2.Title = "长矛攻击左边"
button2.Size = Vector2.New(150, 90)
button2.Position = Vector2.New(120, 300)
button2.Click:Connect(function()
gui_test.Visible = false
attack(-1)
end)
local button3 = create_btn( gui_test )
button3.Title = "长矛攻击右边"
button3.Size = Vector2.New(150, 90)
button3.Position = Vector2.New(380, 300)
button3.Click:Connect(function()
gui_test.Visible = false
attack(1)
end)
end
end
-- 增加碰撞回调函数
actor_yeren.Touched:connect( function(node, pos1, normal1 )
if node then
print("touched :", node , node.Name )
if node.Name == 'player1' then
print("touched player1 :", node, node.Name )
actor_yeren:StopNavigate() -- 停止野人当前行走
open_GUI()
end
end
end )
end
-- 生成一个建筑物,增加一个门,碰撞打开,自动关闭
function MTestcase.case8()
print( "MTestcase.case8" );
-- 增加一个cube立方体
local door_move = SandboxNode.new('GeoSolid', game.WorkSpace )
door_move.Name = 'door_move'
door_move.GeoSolidShape = Enum.GeoSolidShape.Cuboid --Cuboid立方体 Cylinder圆柱体
door_move.LocalPosition = Vector3.new( 750, 250, 500 ); --位置
door_move.LocalScale = Vector3.new( 5, 5, 1 ); --缩放
local TweenService = game:GetService("TweenService")
local tweenInfo = TweenInfo.New(
1, -- Time
Enum.EasingStyle.Linear, -- EasingStyle
Enum.EasingDirection.Out, -- EasingDirection
0 -- DelayTime
-1, -- RepeatCount (小于零时 tween 会无限循环)
true -- Reverses (tween 完成目标后会反转)
)
local tween = TweenService:Create(door_move, tweenInfo, {Position = Vector3.New(750, 600, 500)}) --升高动画
local tween_back = TweenService:Create(door_move, tweenInfo, {Position = Vector3.New(750, 250, 500)}) --降低动画
door_move.Touched:Connect( function(node, pos1, normal1 )
if node.Name == 'player1' then
--print("door touched player1 :", node, node.Name )
tween:Play() --升高
wait(5)
tween_back:Play() --降低
end
end )
end
-- NPC判断与玩家的距离,靠近或者攻击
function MTestcase.case9()
print( "MTestcase.case9" );
-- 增加一个actor
local actor_yeren = SandboxNode.new('Actor', game.WorkSpace ) --actor会按照迷你游戏来表现行为和动画
actor_yeren.Name = 'model_yeren1'
actor_yeren.ModelId = 'sandboxSysId://entity/100028/body.omod' --增加一个野人
actor_yeren.LocalPosition = Vector3.new( 500, 300, 500 ); --位置
actor_yeren.Size = Vector3.new( 50, 200, 50 ) --碰撞盒子的大小
actor_yeren.Center = Vector3.new( 0, 100, 0 ) --盒子中心位置
wait(1)
local player1 = game:GetService("Players").player1
print( 'player1', player1 )
local function cb_walk_attack ()
--距离大于15米就走近, 小于15米就攻击
print("cb_walk_attack:", ( actor_yeren.Position - player1.Position ).length )
if ( actor_yeren.Position - player1.Position ).length > 1500 then
--向玩家靠近
print("NavigateTo:", player1.Position )
actor_yeren:NavigateTo( player1.Position )
else
actor_yeren:StopNavigate()
--朝向玩家
local direction = actor_yeren.Position - player1.Position
direction.y = 0
local _, rotation = Quaternion.LookAt(direction)
actor_yeren.LocalRotation = rotation
--扔出一根长矛
actor_yeren:PlayAnimation( '100105', 1.0, 1 ) --投掷动作
wait(0.5)
local changmao = game.WorkSpace.changmao:Clone()
changmao.Name = 'model_changmao'
changmao.Parent = game.WorkSpace;
--长矛的位置是野人的右手边,计算右边后调整位置
local VECUP = Vector3.new(0, 1, 0)
local right_hand = direction:cross(VECUP) --VECUP
Vector3.Normalize( right_hand )
changmao.LocalPosition = Vector3.new( actor_yeren.LocalPosition.x + right_hand.x*64,
actor_yeren.LocalPosition.y + 200,
actor_yeren.LocalPosition.z + right_hand.z*64 )
changmao.Velocity = (player1.Position - actor_yeren.Position)*1.5 + Vector3.new( 0, 200, 0 )
--调整方向
changmao.Euler = Vector3.new( actor_yeren.Euler.x - 90 , actor_yeren.Euler.y, actor_yeren.Euler.z )
--print ( 'Euler: ', actor_yeren.Euler, ' ', changmao.Euler )
wait(10)
changmao:Destroy()
end
end
-- 一个定时器, 每N秒让野人判断一次行动
local timer = SandboxNode.New("Timer", game.WorkSpace)
timer.Delay = 1 -- 延迟多少秒开始
timer.Loop = true -- 是否循环
timer.Interval = 3 -- 循环间隔多少秒
timer.Callback = cb_walk_attack
timer:Start() -- 启动定时器
print("timer start")
end
-- 给玩家增加一个攻击按钮,点击的时候会向前方攻击
function MTestcase.case10()
print( "MTestcase.case10" );
local player1 = game.WorkSpace.player1
print( 'player1', player1 )
wait(2)
--给模型设置绑点
local attachment= SandboxNode.New('BindAttachment', player1)
attachment.Name = "rightHand"
attachment.BoneName = '101'
attachment.LocalPosition = Vector3.New(0, 0, 0)
attachment.LocalScale = Vector3.New(1, 1, 1)
print( 'attachment', attachment )
-- 增加一个长矛模型 绑定在手上
local changmao_bind = SandboxNode.new('Model', attachment )
changmao_bind.Name = 'model_changmao'
changmao_bind.ModelId = 'sandboxSysId://itemmods/12004/body.omod'
changmao_bind.EnablePhysics = false
changmao_bind.CanCollide = false
changmao_bind.CanTouch = false
changmao_bind.LocalPosition = Vector3.new(0,0,0)
changmao_bind.LocalEuler = Vector3.new(0,90,0)
print( 'changmao_bind', changmao_bind )
wait(1)
-- 增加一个按钮
local ui_root = SandboxNode.New('UIRoot', game.WorkSpace)
ui_root.Name = 'ui_root'
local attack_button = SandboxNode.New('UIButton', ui_root )
attack_button.Icon = 'RainbowId&filetype=5://230837040806105088' --按钮背景
attack_button.FillColor = ColorQuad.new( 0,0,0,0 )
attack_button.TitleSize = 20
attack_button.Title = "attack"
attack_button.DownEffect = Enum.DownEffect.ScaledEffect
attack_button.LayoutHRelation = Enum.LayoutHRelation.Right --靠右
attack_button.LayoutVRelation = Enum.LayoutVRelation.Bottom --靠下
attack_button.Size = Vector2.New(100, 100)
local screen_size = game:GetService( 'WorldService' ):GetUISize()
attack_button.Position = Vector2.New( screen_size.x - 260, screen_size.y - 100)
attack_button.Click:Connect( function()
print("attack")
--扔出一根长矛
player1:PlayAnimation( '100105', 1.0, 1 ) --投掷动作
wait(0.5)
-- 获得玩家当前的朝向
local v3_direction = player1.Rotation:LookDir();
--长矛的位置是右手边,计算右边后调整位置
local VECUP = Vector3.new(0, 1, 0)
local right_hand = v3_direction:cross(VECUP) --VECUP
Vector3.Normalize( right_hand )
--print( 'player1.LocalPosition=', player1.LocalPosition, right_hand )
local changmao = game.WorkSpace.changmao:Clone()
changmao.Parent = game.WorkSpace;
changmao.CollideGroupID = 1
changmao.LocalPosition = Vector3.new( player1.LocalPosition.x + right_hand.x*64,
player1.LocalPosition.y + 120,
player1.LocalPosition.z + right_hand.z*64 )
--changmao.LocalEuler = Vector3.new( player1.LocalEuler.x - 90, player1.LocalEuler.y, player1.LocalEuler.z )
changmao.Euler = Vector3.new( player1.Euler.x - 90 , player1.Euler.y, player1.Euler.z )
print( 'changmao Euler=', changmao.Euler, ' ', changmao.LocalEuler )
local direction = player1.Rotation:LookDir();
--print( 'player1.LocalPosition=', player1.LocalPosition, right_hand )
changmao.Velocity = direction * -2000 + Vector3.new( -500, 200, 0 ); -- 目前z轴为负,需要乘负数来转换
wait(10)
changmao:Destroy()
end )
end
-- 序列化一个table数据,并存在云端
function MTestcase.case11()
print( "MTestcase.case11" );
-- 增加一个cube立方体
local obj = SandboxNode.new('GeoSolid', game.WorkSpace )
obj.Name = 'cube_test3'
obj.GeoSolidShape = Enum.GeoSolidShape.Cuboid --Cuboid立方体
obj.LocalPosition = Vector3.new( 0, 300, 0 ); --位置
obj.Color = ColorQuad.new( 0, 0, 255, 255 ); --蓝色
local table_name_key = 'key1234567_obj_position1' --存取的key值
-- 取云端数据
local CloudService = game:GetService("CloudService")
local ret1, table_data = CloudService:GetTable( table_name_key )
print( '====GetTable ', ret1, ' , ', table_data )
if ret1 and type(table_data) == 'table' then
print_table( table_data, 'ret_table= ' ) --成功取到了数据
else
--取不到则新建数据 false, invalid base64 data
table_data = { msg='test', value=1, pos={ x=300, y=300, z=300 } }
print_table( table_data, 'new create ret_table= ' ) --首次新建
end
table_data.value = table_data.value + 1 --每次运行:保存的value值递增1
table_data.pos.y = table_data.pos.y + 100 --每次运行:坐标y值每次递增100
obj.LocalPosition = Vector3.new( table_data.pos.x, table_data.pos.y, table_data.pos.z ) --使用数据
--存数据到云端
local ret2 = CloudService:SetTable( table_name_key, table_data )
print( '====SetTable ', ret2 )
end
-- 序列化一个table数据,并存在云端(异步模式)
function MTestcase.case12()
print( "MTestcase.case12" );
-- 增加一个cube立方体
local obj = SandboxNode.new('GeoSolid', game.WorkSpace )
obj.Name = 'cube_test3'
obj.GeoSolidShape = Enum.GeoSolidShape.Cuboid --Cuboid立方体
obj.LocalPosition = Vector3.new( 0, 300, 0 ); --位置
obj.Color = ColorQuad.new( 0, 0, 255, 255 ); --蓝色
local table_name_key = 'key1234567_obj_position2' --存取的key值
-- 取云端数据
local CloudService = game:GetService("CloudService")
CloudService:GetTableAsync( table_name_key,
-- GetTableAsync的回调函数
function( ret_read, table_data )
print( '====GetTable1 ', ret_read, ' , ', table_data )
if ret_read and type(table_data) == 'table' then
print_table( table_data, 'ret_table= ' ) --成功取到了数据
else
--取不到则新建数据 false, invalid base64 data
table_data = { msg='test', value=1, pos={ x=300, y=300, z=300 } }
print_table( table_data, 'new create ret_table= ' ) --首次新建
end
table_data.value = table_data.value + 1 --每次运行:保存的value值递增1
table_data.pos.y = table_data.pos.y + 100 --每次运行:坐标y值每次递增100
obj.LocalPosition = Vector3.new( table_data.pos.x, table_data.pos.y, table_data.pos.z ) --使用数据
--存数据到云端
CloudService:SetTableAsync( table_name_key, table_data,
--SetTableAsync的回调函数
function( ret_save )
print( '====SetTable1 ', ret_save )
end
);
end
);
end
-- 建立一个立方体区域,判断物体进入
function MTestcase.case13()
print( "MTestcase.case13" );
local area = SandboxNode.New('Area', game.WorkSpace) --创建AreaNode节点
--创建Actor实例
--local actorNode = SandboxNode.New('Actor')
--actorNode.Name = "my_actor"
area.Beg = Vector3.new( -100, 0, -100 );
area.End = Vector3.new( 800, 500, 800 );
area.EffectWidth = 16;
area.Show = true --显示
--actorNode进入区域
area.EnterNode:Connect(function(node)
print("node 进入区域", node)
end)
area.LeaveNode:Connect(function(node)
print("Node 离开区域", node)
end)
end
-- 鼠标点击选中物体判断 pickup
function MTestcase.case14()
print( "MTestcase.case14" );
--使用屏幕中心点点击,使用 camera_win_size
--local camera_win_size = game.WorkSpace.CurrentCamera.WindowSize
--local center_pos_x = camera_win_size.x * 0.5
--local center_pos_y = camera_win_size.y * 0.5
local inputservice = game:GetService("UserInputService")
--按下
local function inputBegan( inputObj, bGameProcessd )
log( "InputBegan", inputObj, bGameProcessd, inputObj.UserInputState, inputObj.UserInputType )
--print( inputObj.UserInputState ) -- 0 这里都是InputBegan
if inputObj.UserInputType == Enum.UserInputType.Keyboard.Value then
print( 'keyPressed KeyCode: ', inputObj.KeyCode ) -- 鼠标左键按下时位置
end
if inputObj.UserInputType == Enum.UserInputType.MouseButton1.Value then
print( 'left pressed pos: ', inputObj.Position.x, ' ', inputObj.Position.y ) -- 鼠标左键按下时位置
local obj_list = { --表示只在哪些obj里面查找
--game.WorkSpace.g0,
--game.WorkSpace.g0.g0_copy_2,
}
local rets = inputservice:PickObjects( inputObj.Position.x, inputObj.Position.y, obj_list )
log( 'GetCursorPick[', rets, '] [', obj_list, ']' )
if rets then
--改动框的显示,明确是否被选中
if rets.CubeBorderEnable == false then
rets.CubeBorderEnable = true
else
rets.CubeBorderEnable = false
end
end
end
end
inputservice.InputBegan:Connect(inputBegan)
--移动
--local function onInputChanged( inputObj, passed )
--gg.log ( '2 onInputChanged', inputObj, inputObj.UserInputType, passed )
--end
--inputservice.InputChanged:Connect( onInputChanged )
--抬起
--local function onInputEnded( inputObj, passed )
--gg.log ( '3 onInputEnded', inputObj, inputObj.UserInputType, passed )
--end
--inputservice.InputEnded:Connect( onInputEnded )
end
-- 检查射线ray碰撞物体
function MTestcase.case15()
print( "MTestcase.case15" );
--按下空格键的时候,就发出一条射线
local function test_function()
--从玩家所在点发出射线
--如果使用camera射线,则 game.WorkSpace.player1 改为 game.WorkSpace.Camera
local position = game.WorkSpace.player1.LocalPosition
local orgin = Vector3.new( position.x, position.y+100, position.z ) --玩家坐标在脚底,抬高1米
--玩家面向的方向为射线的方向
--方法1: 取玩家的方向四元数,再转换成标准向量
--local q4_rotate = game.WorkSpace.player1.Rotation -- Quaternion 玩家朝向四元数
--log( 'Rotation=1=', q4_rotate )
--方法2: 取玩家的欧拉角vector3,再转换成四元数,再转换成标准向量
local vec3_euler = game.WorkSpace.player1.Euler
local q4_rotate = Quaternion.FromEuler( vec3_euler.x, vec3_euler.y, vec3_euler.z )
--log ( 'Euler=2=', vec3_euler, q4_rotate )
local vec3_direction = q4_rotate:LookDir() * -1 -- 四元数转成vector3向量 (左右手坐标系转换,需要乘以负1)
--RaycastClosest 射线检测最近物体
--RaycastAll 射线检测所有碰到的物体
--参数1 射线发出点
--参数2 射线发出方向
--参数3 射线距离
--参数4 CollideGroupID碰撞组id过滤 { id1, id2, id3 }
local ret_table = game:GetService( 'WorldService' ):RaycastAll( orgin, vec3_direction, 3200, true, {1,2,3,4,5} )
log ( 'debug_check_ray=3=', orgin, vec3_direction, ret_table )
print_table( ret_table, 'all_ray_objs' ) --打印整个table(所有射线碰到的物体)
--返回值
--{ 1={normal={0, 0, -0} distance=620 position={-550, 100, 818}
--obj=[node(67930)(Model):野人伍长]} 2={normal={0, 0, -1} distance=1409 position={-845, 100, 1550}
--obj=[node(67926)(GeoSolid):g0_copy墙壁 ]} }
end
-- 按空格键触发上面的测试函数
local function inputBegan( inputObj, bGameProcessd )
if inputObj.UserInputType == Enum.UserInputType.Keyboard.Value then
if inputObj.KeyCode == Enum.KeyCode.Space.Value then -- Enum.KeyCode.Space.Value = 32
test_function()
end
end
end
game:GetService("UserInputService").InputBegan:Connect(inputBegan)
end
-- 播放声音特效
function MTestcase.case16()
print( "MTestcase.case16" );
local function test_function()
local sound = SandboxNode.New('Sound', game.WorkSpace ) --创建Sound节点
sound.SoundPath = 'sandboxSysId://sounds/ent/yanhua/blast2.ogg' --设置资源路径 (气泡音)
sound.IsLoop = false --是否循环播放
--sound.RollOffMode = Enum.RollOffMode.Linear --设置声音衰减模式
--sound.RollOffMinDistance = 300
--sound.RollOffMaxDistance = 700
sound:PlaySound() --播放函数
print( 'client playSoundEffect');
--播放结束事件回调函数
sound.PlayFinish:Connect(function(node)
node:Destroy()
print("sound is Destroy")
end)
end
-- 按空格键触发上面的测试函数
local function inputBegan( inputObj, bGameProcessd )
if inputObj.UserInputType == Enum.UserInputType.Keyboard.Value then
log( 'keyPressed KeyCode: ', inputObj.KeyCode, bGameProcessd )
if inputObj.KeyCode == Enum.KeyCode.Space.Value then -- Enum.KeyCode.Space.Value = 32
test_function()
end
end
end
game:GetService("UserInputService").InputBegan:Connect(inputBegan)
end