NeoAxis 摄像机绕物体旋转实现!
在3维产品中,尤其在产品展示的时候,让摄像机绕着一个物体旋转是非常常见的!让摄像机绕着一个物体旋转需要知道的参数有圆点,半径,弧度。
摄像机的旋转无非就是路径的变换,也就是摄像机Position变换,然后让摄像机看着中心点(圆点)就行了,关键是这个旋转的路径,路径可以是一个圆弧,这个圆弧实现可以用:圆弧路径=半径*弧度算出,在确定了圆点,就得到了一条准确的圆弧。
下面在NeoAxis中实现这个摄像机绕物体旋转,我们在Game中(Game程序集)新建一个类MyGameWindow继承自EControl,然后定义三个变量
Vec3 cameraLookAt;//摄像机看的目标(摄像机旋转的中心点,绕某个物体旋转就是某个物体的Position)
float cameraDistance;//摄像机旋转半径
SphereDir cameraDirection;//摄像机方向
重写EControl父类的OnAttach方法,OnAttach方法在该控件被加载到游戏中时被调用,可以进行一些初始化工作。
protected override void OnAttach()
{
base.OnAttach();
cameraLookAt = new Vec3(0, 0, 0);
cameraDistance = 1f;
cameraDirection = new SphereDir(0.5f, 0.5f);
}
然后重写EControl的OnRende方法,OnRender方法以帧率速率被调用,用来画出需要显示的内容,我们在OnRender中实现摄像机的运动
protected override void OnRender()
{
base.OnRender();
//默认设置
Camera camera = RendererWorld.Instance.DefaultCamera;
camera.NearClipDistance = 0.1f;
camera.FarClipDistance = 1000;
camera.Fov = 90;
camera.FixedUp = Vec3.ZAxis;
////////改变的设置
//摄像机的位置= 圆点位置 + 摄像机的移动角度 * 半径
camera.Position = cameraLookAt + cameraDirection.GetVector() * cameraDistance;
//摄像机始终看着圆点
camera.LookAt(cameraLookAt);
}
默认设置是摄像机的一些基本属性设置不用管,在改变的设置下,实现了我们的摄像机位置变换和观察方向设置,
现在我们调用这个类查看效果,打开Program类,在该类下是游戏启动的入口(以Demo为例)首先启动Main方法,然后加载Main2,在Main2中实现一些游戏的初始化工作,加载EngineApp初始化引擎程序,然后加载到了GameEngineApp类中,加载完成游戏开始,我们可以在GameEngineApp中调用我们的这个控制类,在GameEngineApp中有个方法在加载地图的时候(开始一个新游戏)调用了一个CreateGameWindowForMap方法,我们可以在这个方法中加载我们的控制类,这样在任何游戏中我们的类都是起作用的

但是这样会取消Demo中的游戏控制设置,也可以在需要的时候在其他地方加载,修改完成,运行游戏,就可以看到我们的摄像机在初始位置出现。接下来要做的就是改变摄像机的位置,也就是改变半径和弧度,例如:
我们想鼠标右键并移动控制摄像机的旋转,鼠标滚轮控制半径,就可以重写OnMouseMove方法进行实现
protected override bool OnMouseMove()
{
if (EngineApp.Instance.IsMouseButtonPressed(EMouseButtons.Right))
{
cameraDirection.Horizontal -= MousePosition.X;
cameraDirection.Vertical += MousePosition.Y;
}
}
return base.OnMouseMove();
}
protected override bool OnMouseWheel(int delta)
{
cameraDistance *= 1.0f + (float)delta / 1000.0f;
if (cameraDistance < .01f)
cameraDistance = .01f;
return base.OnMouseWheel(delta);
}
在滚轮中我们限定了半径最小不能小于0.1,至少得有半径是吧!
运行游戏,我们按下鼠标右键并移动,摄像机是动了,但是非常快,显得毫无规律,但是滚轮控制半径(距离)实现了,接下来就要限定鼠标控制旋转的大小了!为什么会太快?我们定义的摄像机旋转是一个弧度类型,一个圆最多也就360度也就是一个派(圆周率3.1415926)在NeoAxis中用MathFunctions.PI 表示,但是我们的旋转 cameraDirection.Horizontal -= MousePosition.X;
cameraDirection.Vertical += MousePosition.Y;直接等于了鼠标移动的像素值,这样太快了。
cameraDirection是一个SphereDir类型包含了两个参数 Horizontal 水平弧度和Vertical 弧度,我们需要改变的最大值也就是MathFunctions.PI,360度,我们要做的就是在鼠标点下去的时候得到鼠标的位置,然后在移动鼠标的时候用鼠标的移动位移和这个位置做比较,以此限定旋转的弧度,可以利用NeoAxis 中EngineApp里面的MouseRelativeMode属性控制,MouseRelativeMode是一个bool类型的属性,用来控制鼠标光标,如果为true光标不显示,并且鼠标光标不移动,反之为正常鼠标光标。
我们可以定义一个bool变量表示是否鼠标按下右键,在定义一个Vec2类型的变量在鼠标按下右键的时候得到鼠标的位置,在OnMouseMove中用这个位置和鼠标当前位置得到鼠标的移动绝对值限定旋转的度数。
在OnMouseDown中我们得到鼠标按下时的位置并设置鼠标是否按下了右键(是否准备旋转),在OnMouseUp中释放鼠标按下了右键的变量
在OnMouseMove中判断鼠标位移,设置鼠标的MouseRelativeMode限定鼠标,同时需要一个变量表示是否可以旋转
那么先定义三个变量
bool prepareToCameraRotating;//是否准备旋转
Vec2 prepareToCameraRotatingMouseStart;//准备旋转开始时鼠标的位置
bool cameraRotating;//是否旋转镜头
然后在鼠标事件中控制
protected override bool OnMouseDown(EMouseButtons button)
{
if (button == EMouseButtons.Right)//按下了右键
{
prepareToCameraRotating = true;//准备旋转为true
prepareToCameraRotatingMouseStart = EngineApp.Instance.MousePosition;//得到鼠标位置
}
return base.OnMouseDown(button);
}
protected override bool OnMouseUp(EMouseButtons button)
{
if (button == EMouseButtons.Right)//释放了右键
{
prepareToCameraRotating = false;//准备旋转为false
if (cameraRotating)//如果正在旋转
{
EngineApp.Instance.MouseRelativeMode = false;//显示光标
cameraRotating = false;//不旋转
}
}
return base.OnMouseUp(button);
}
protected override bool OnMouseMove()
{
if (prepareToCameraRotating)//如果准备旋转(按下了右键)
{
//得到鼠标的移动位置, 当前鼠标位置-按下键时鼠标的位置 * 屏幕分辨率的大小
Vec2 diffPixels = (MousePosition - prepareToCameraRotatingMouseStart) *
new Vec2(EngineApp.Instance.VideoMode.X, EngineApp.Instance.VideoMode.Y);
//如果鼠标X方向或者Y方向的移动距离绝对值 > 3 (3.14为一周这里只转大半圈)
if (Math.Abs(diffPixels.X) >= 3 || Math.Abs(diffPixels.Y) >= 3)
{
prepareToCameraRotating = false;//准备旋转结束
cameraRotating = true;//旋转开始
EngineApp.Instance.MouseRelativeMode = true;//隐藏光标,并且光标位置不能移动
return true;
}
}
//如果开始旋转
if (cameraRotating)
{
//判断是否按着右键
if (EngineApp.Instance.IsMouseButtonPressed(EMouseButtons.Right))
{
cameraDirection.Horizontal -= MousePosition.X;//旋转水平方向
cameraDirection.Vertical += MousePosition.Y;//旋转垂直方向
//float limit = MathFunctions.PI / 2 - .01f;
//if (cameraDirection.Vertical > limit)
// cameraDirection.Vertical = limit;
//if (cameraDirection.Vertical < -limit)
// cameraDirection.Vertical = -limit;
}
}
return base.OnMouseMove();
}
注释部分
//float limit = MathFunctions.PI / 2 - .01f;
//if (cameraDirection.Vertical > limit)
// cameraDirection.Vertical = limit;
//if (cameraDirection.Vertical < -limit)
// cameraDirection.Vertical = -limit;
限定鼠标垂直位置只能移动小半圈,一圈为MathFunction.PI ,除以2 在减去一点浮动!
大功告成! 运行游戏,摄像机按照我们设想的方法移动,但是如果滚动滚轮,一直滚,一直滚,越来越远了!
我们可以在OnTike中限定,或者直接在OnMouseWheel中限定!
全部代码如下
using System;
using System.Collections.Generic;
using System.Text;
using Engine.UISystem;
using Engine.MathEx;
using Engine;
using Engine.Renderer;
using Engine.MapSystem;
namespace Game
{
public class MyGameWindow : EControl
{
// Camera
Vec3 cameraLookAt;//摄像机看的目标(摄像机旋转的中心点,绕某个物体旋转就是某个物体的Position)
float cameraDistance;//摄像机旋转半径
SphereDir cameraDirection;//摄像机方向
bool prepareToCameraRotating;//是否准备旋转
Vec2 prepareToCameraRotatingMouseStart;//准备旋转开始时鼠标的位置
bool cameraRotating;//是否旋转镜头
protected override void OnAttach()
{
base.OnAttach();
cameraLookAt = new Vec3(0, 0, 0);
cameraDistance = 1f;
cameraDirection = new SphereDir(0.5f, 0.5f);
}
protected override bool OnMouseDown(EMouseButtons button)
{
if (button == EMouseButtons.Right)//按下了右键
{
prepareToCameraRotating = true;//准备旋转为true
prepareToCameraRotatingMouseStart = EngineApp.Instance.MousePosition;//得到鼠标位置
}
return base.OnMouseDown(button);
}
protected override bool OnMouseUp(EMouseButtons button)
{
if (button == EMouseButtons.Right)//释放了右键
{
prepareToCameraRotating = false;//准备旋转为false
if (cameraRotating)//如果正在旋转
{
EngineApp.Instance.MouseRelativeMode = false;//显示光标
cameraRotating = false;//不旋转
}
}
return base.OnMouseUp(button);
}
protected override bool OnMouseMove()
{
if (prepareToCameraRotating)//如果准备旋转(按下了右键)
{
//得到鼠标的移动位置, 当前鼠标位置-按下键时鼠标的位置 * 屏幕分辨率的大小
Vec2 diffPixels = (MousePosition - prepareToCameraRotatingMouseStart) *
new Vec2(EngineApp.Instance.VideoMode.X, EngineApp.Instance.VideoMode.Y);
//如果鼠标X方向或者Y方向的移动距离绝对值 > 3 (3.14为一周这里只转大半圈)
if (Math.Abs(diffPixels.X) >= 3 || Math.Abs(diffPixels.Y) >= 3)
{
prepareToCameraRotating = false;//准备旋转结束
cameraRotating = true;//旋转开始
EngineApp.Instance.MouseRelativeMode = true;//隐藏光标,并且光标位置不能移动
return true;
}
}
//如果开始旋转
if (cameraRotating)
{
//判断是否按着右键
if (EngineApp.Instance.IsMouseButtonPressed(EMouseButtons.Right))
{
cameraDirection.Horizontal -= MousePosition.X;//旋转水平方向
cameraDirection.Vertical += MousePosition.Y;//旋转垂直方向
float limit = MathFunctions.PI / 2 - .01f;
if (cameraDirection.Vertical > limit)
cameraDirection.Vertical = limit;
if (cameraDirection.Vertical < -limit)
cameraDirection.Vertical = -limit;
}
}
return base.OnMouseMove();
}
protected override bool OnMouseWheel(int delta)
{
cameraDistance *= 1.0f + (float)delta / 1000.0f;
if (cameraDistance < .01f)
cameraDistance = .01f;
if (cameraDistance >= 10)
cameraDistance = 10;
return base.OnMouseWheel(delta);
}
protected override void OnRender()
{
base.OnRender();
//默认设置
Camera camera = RendererWorld.Instance.DefaultCamera;
camera.NearClipDistance = 0.1f;
camera.FarClipDistance = 1000;
camera.Fov = 90;
camera.FixedUp = Vec3.ZAxis;
////////改变的设置
//摄像机的位置= 圆点位置 + 摄像机的移动角度 * 半径
camera.Position = cameraLookAt + cameraDirection.GetVector() * cameraDistance;
//摄像机始终看着圆点
camera.LookAt(cameraLookAt);
}
}
}
在这里我们的摄像机一直看着Vec3(0,0,0)位置,如果想看着你想要看的物体,只需要在初始化cameraLookAt的时候设定这个位置为你的物体位置就行了!
浙公网安备 33010602011771号