UDN
Search public documentation:

DevelopmentKitGemsCreatingAMouseInterfaceCH
English Translation
日本語訳
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 主页 > 虚幻开发工具包精华文章 > 创建鼠标接口
UE3 主页 > 用户界面 & HUD > 创建鼠标接口

创建鼠标接口


最后一次测试是在2011年4月份的UDK版本上进行的。
可以与 PC 兼容

概述


默认情况下,虚幻引擎是作为第一人称射击游戏引擎设计的。但是,只要代码正确,完全可以使用它创建很多其他游戏类型,例如,实时策略游戏。

在屏幕上获取光标


将光标添加到屏幕上,同时添加一些新的脚本。因为 UIScene 已经删除,所以以前用来提取鼠标位置的方法现在失效了。There are two methods that you could do this; Unrealscript and ScaleForm.

Unrealscript

可以创建属于您自己的鼠标位置代码。通过添加一个可以对 aMouseXaMouseY 进行调查的自定义玩家输入创建,以此查看是否有所改动。如果有过改动,将其添加给 MousePosition 变量,然后对其进行限定使鼠标光标始终在屏幕边界内。

这是新的游戏信息。它是可以定义要使用的 HUD 类和 PlayerController 类的简单 GameInfo。

MouseInterfaceGameInfo.uc
class MouseInterfaceGameInfo extends GameInfo;

defaultproperties
{
  // 将 HUD 类型设置为鼠标接口 HUD
  HUDType=class'MouseInterfaceHUD'
  // 将玩家控制器设置为鼠标接口 Player Controller
  PlayerControllerClass=class'MouseInterfacePlayerController'
}

这是将会用于渲染屏幕上的鼠标光标的新 hud。同时您可以使用您想要使用的材质。

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 这个纹理可以代表屏幕上的光标
var const Texture2D CursorTexture;
// 光标的颜色
var const Color CursorColor;

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;

  // 确保我们有一个有效的 PlayerOwner 和 CursorTexture
  if (PlayerOwner != None && CursorTexture != None)
  {
    // 转换获取 MouseInterfacePlayerInput
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      // 将画布位置设置为鼠标位置
      Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
      // 设置光标颜色
      Canvas.DrawColor = CursorColor;
      // 绘制屏幕上的纹理
      Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
    }
  }

  Super.PostRender();
}

defaultproperties
{
  CursorColor=(R=255,G=255,B=255,A=255)
  CursorTexture=Texture2D'EngineResources.Cursors.Arrow'
}

代码后面的逻辑规则如下所示:

  • 执行 PostRender 的时候,我们会从 PlayerOwner 中获取新的 PlayerInput,其中 PlayerOwner 就是一个 PlayerController。
  • 如果我们具有新的 PlayerInput,那么我们会将 Canvas 位置设置为存储在我们的新 PlayerInput 内的 MousePosition。
  • 然后我们会将颜色设置为在默认属性中定义的光标颜色。
  • 最后,我们会描画代表鼠标光标的纹理。

它是可以定义要使用的新 PlayerInput 的新玩家控制器。将 UpdateRotation 存根为这篇精华文章不需要的初始函数。

MouseInterfacePlayerController.uc
class MouseInterfacePlayerController extends PlayerController;

//  清空这个函数
function UpdateRotation(float DeltaTime);

defaultproperties
{
  // 将输入类设置为鼠标接口玩家输入
  InputClass=class'MouseInterfacePlayerInput'
}

这是新的玩家输入。

MouseInterfacePlayerInput.uc
class MouseInterfacePlayerInput extends PlayerInput;

// 存储鼠标位置。设置为 private write 权限,因为我们不希望其他类修改它,但是仍然允许其他类进行访问。
var PrivateWrite IntPoint MousePosition;

event PlayerInput(float DeltaTime)
{
  // 处理鼠标
  // 确保我们有一个有效的 HUD
  if (myHUD != None)
  {
    // 将 aMouseX 添加到鼠标位置,并将其限定在视图宽度范围内
    MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, myHUD.SizeX);
    // 将 aMouseY 添加到鼠标位置,并将其限定在视图高度范围内
    MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, myHUD.SizeY);
  }

  Super.PlayerInput(DeltaTime);
}

defaultproperties
{
}

代码后面的逻辑规则如下所示:

  • 在执行 PlayerInput 的时候,我们会处理调整鼠标位置。
  • 需要使用 HUD 将鼠标位置限定在视图范围内。因为 PlayerInput 是一个位于 PlayerController 内部的对象,所以我们可以直接访问 PlayerController 内的变量。
  • 使用限定在输入配置内的 aMouseX 和 aMouseY,我们会将这些值添加到我们的 MousePosition 变量中。我们必须反转垂直计算结果才能制作出正确的效果。
  • 通过限定这个结果,我们可以确保鼠标位置始终在视图范围内。

编译。创建一个新的地图,然后将这个 PIE GameInfo 设置为 MouseInterfaceGameInfo 并进行测试。您应该可以看到并四处移动光标。在下面的图片中,光标位于红色圆圈内。

MouseInterfacePhaseOne.jpg

ScaleForm

同时可以使用 ScaleForm 对鼠标位置进行调查。在这个篇特殊的精华文章中,大部分脚本保持不变。在您使用 ScaleForm 的情况下例外,上面创建自定义玩家输入类不再对鼠标变更进行调查。相反,当 ScaleForm 检测到鼠标位置发生变化时,它会将新的鼠标位置传递给 Unrealscript。

这是在 Adobe Flash 中编写的 action script。鼠标移动后,action script 会将图层调用光标的位置设置为 ScaleForm 的鼠标位置,而且它还会发送 Unrealscript ScaleForm 的鼠标位置。

MouseInterfaceCursor.fla > actions: Frame 1
import flash.external.ExternalInterface;

// 隐藏标准的 "Windows" 指针。
Mouse.hide();

var mouseListener:Object = new Object();

mouseListener.onMouseMove = function() {
  // 将光标实例位置设置为鼠标位置
  cursor._x = _root._xmouse;
  cursor._y = _root._ymouse;

  // 将新的鼠标坐标传递给 Unrealscript
  ExternalInterface.call("UpdateMousePosition", _root._xmouse, _root._ymouse);

  updateAfterEvent();
};

Mouse.addListener(mouseListener);

这是 ScaleForm 动画的 Unrealscript。它包含 ScaleForm 用来传递它的鼠标位置值的事件。

MouseInterfaceGFx.uc
class MouseInterfaceGFx extends GFxMoviePlayer;

var MouseInterfaceHUD MouseInterfaceHUD;

function Init(optional LocalPlayer LocalPlayer)
{
  // 初始化 ScaleForm 动画
  Super.Init(LocalPlayer);

  Start();
  Advance(0);
}

event UpdateMousePosition(float X, float Y)
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;

  if (MouseInterfaceHUD != None && MouseInterfaceHUD.PlayerOwner != None)
  {
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(MouseInterfaceHUD.PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      MouseInterfacePlayerInput.SetMousePosition(X, Y);
    }
  }
}

defaultproperties
{
  bDisplayWithHudOff=false
  TimingMode=TM_Game
  MovieInfo=SwfMovie'MouseInterfaceContent.MouseInterfaceCursor'
  bPauseGameWhileActive=false
}

HUD 是创建和初始化 ScaleForm 的地方。HUD 被销毁后,还请确保关闭了它进行的所有 ScaleForm 引用。*PreCalcValues* 是可以检测出屏幕的分斌率是否发生过变化的函数。这个函数非常重要,它可以确保通知 ScaleForm 所进行的这些变化,因此它会重新设置它的视窗、缩放模式和对齐方式,以便与分辨率匹配。最后,因为 ScaleForm 现在渲染出光标,会通知 HUD 不要渲染这个光标。

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 这个纹理可以代表屏幕上的光标
var const Texture2D CursorTexture;
// 光标的颜色
var const Color CursorColor;
// 使用 ScaleForm?
var bool UsingScaleForm;
// Scaleform 鼠标动画
var MouseInterfaceGFx MouseInterfaceGFx;

simulated event PostBeginPlay()
{
  Super.PostBeginPlay();

  // 如果我们使用的是 ScaleForm,那么创建 ScaleForm 动画
  if (UsingScaleForm)
  {
    MouseInterfaceGFx = new () class'MouseInterfaceGFx';
    if (MouseInterfaceGFx != None)
    {
      MouseInterfaceGFx.MouseInterfaceHUD = Self;
      MouseInterfaceGFx.SetTimingMode(TM_Game);

      MouseInterfaceGFx.Init(class'Engine'.static.GetEngine().GamePlayers[MouseInterfaceGFx.LocalPlayerOwnerIndex]);
    }
  }
}

simulated event Destroyed()
{
  Super.Destroyed();

  // 如果 ScaleForm 动画存在,那么销毁它
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.Close(true);
    MouseInterfaceGFx = None;
  }
}

function PreCalcValues()
{
  Super.PreCalcValues();

  // 如果 ScaleForm 动画存在,那么重新设置它的视窗、缩放模式和对齐方式,一以便与屏幕分辨率匹配
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.SetViewport(0, 0, SizeX, SizeY);
    MouseInterfaceGFx.SetViewScaleMode(SM_NoScale);
    MouseInterfaceGFx.SetAlignment(Align_TopLeft);
  }
}

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local Vector HitLocation, HitNormal;

  Super.PostRender();

  // 确保我们使用的不是 ScaleForm,同时我们有一个有效的光标
  if (!UsingScaleForm && CursorTexture != None)
  {
    // 确保我们有一个有效的 PlayerOwner
    if (PlayerOwner != None)
    {
      // 转换获取 MouseInterfacePlayerInput
      MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

      // 如果我们使用的不是 scale form,而且我们有一个有效的光标贴图,那么渲染它
      if (MouseInterfacePlayerInput != None)
      {
        // 将画布位置设置为鼠标位置
        Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
        // 设置光标颜色
        Canvas.DrawColor = CursorColor;
        // 绘制屏幕上的纹理
        Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
      }
    }
  }

  Super.PostRender();
}

最后,将一个新的函数添加到允许其他类写到 MousePosition 变量的 MouseInterfacePlayerInput。按计划进行这项操作可以确保鼠标位置始终位于 Canvas 的渲染边界内。

MouseInterfacePlayerInput.uc
class MouseInterfacePlayerInput extends PlayerInput;

var PrivateWrite IntPoint MousePosition;

event PlayerInput(float DeltaTime)
{
  local MouseInterfaceHUD MouseInterfaceHUD;

  // 处理鼠标移动
  // 查看我们是否有合适的 HUD 类
  MouseInterfaceHUD = MouseInterfaceHUD(MyHUD);
  if (MouseInterfaceHUD != None)
  {
    if (!MouseInterfaceHUD.UsingScaleForm)
    {
      // 如果我们使用的不是 ScaleForm,那么直接阅读鼠标输入
      // 将 aMouseX 添加到鼠标位置,并将其限定在视图宽度范围内
      MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, MouseInterfaceHUD.SizeX);
      // 将 aMouseY 添加到鼠标位置,并将其限定在视图高度范围内
      MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, MouseInterfaceHUD.SizeY);
    }
  }

  Super.PlayerInput(DeltaTime);
}

function SetMousePosition(int X, int Y)
{
  if (MyHUD != None)
  {
    MousePosition.X = Clamp(X, 0, MyHUD.SizeX);
    MousePosition.Y = Clamp(Y, 0, MyHUD.SizeY);
  }
}

defaultproperties
{
}

所以,在这里 ScaleForm 没怎么改动逻辑方法。这里的主要改动是用来检测鼠标当前位置的方法。

相关主题

我只想要鼠标 2D 坐标!

要获取鼠标 2D 坐标,您只需要访问 MouseInterfacePlayerInput。

要在 HUD 内获取鼠标 2D 坐标。

MouseInterfaceHUD.uc
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local IntPoint MousePosition;

  // 确保我们有一个有效的 PlayerOwner
  if (PlayerOwner != None)
  {
    // 转换获取 MouseInterfacePlayerInput
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      // 要获取/使用鼠标 X 位置
      MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
      // 要获取/使用鼠标 Y 位置
      MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
    }
  }

要在 PlayerController 内获取它。

MouseInterfacePlayerController.uc
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local IntPoint MousePosition;

  // 转换获取 MouseInterfacePlayerInput
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerInput);

  if (MouseInterfacePlayerInput != None)
  {
    // 要获取/使用鼠标 X 位置
    MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
    // 要获取/使用鼠标 Y 位置
    MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  }

相关主题

添加鼠标交互接口


这是将会实现鼠标可交互 actor 的接口。将这个接口添加到您的软件包。如果您希望执行更多诸如其他按钮或鼠标沿轴运动这样的鼠标函数,那么将其追加到这个接口中,这样每个鼠标可交互 actor 将会继承它。

MouseInterfaceInteractionInterface.uc
interface MouseInterfaceInteractionInterface;

// 在按下鼠标左键时调用
function MouseLeftPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 在释放鼠标左键时调用
function MouseLeftReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 在按下鼠标右键时调用
function MouseRightPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 在释放鼠标右键时调用
function MouseRightReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 在按下鼠标中键时调用
function MouseMiddlePressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 在释放鼠标中键时调用
function MouseMiddleReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 在向上滚动鼠标中键时调用
function MouseScrollUp(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 在向下滚动鼠标中键时调用
function MouseScrollDown(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 当鼠标在 actor 上方移动时调用
function MouseOver(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 当鼠标移出这个 actor(前提是鼠标之前在这个 actor 上)时调用
function MouseOut(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 返回鼠标轨迹的点击位置
function Vector GetHitLocation();

// 返回鼠标轨迹的点击法线
function Vector GetHitNormal();

// 返回通过在画布内反投射计算得出的鼠标世界原点
function Vector GetMouseWorldOrigin();

// 返回通过在画布内反投射计算得出的鼠标世界方向
function Vector GetMouseWorldDirection();

相关主题

使用 PlayerController 和 HUD 提供鼠标交互


鼠标输入可以通过使用 HUD 内的待定布尔变量进行处理。这样做的原因是反投射需要访问这个画布。这个画布需要根据每一帧进行重新分配,所以在几帧过程中不能保留对它的引用。因此,在使用鼠标按钮或鼠标滑轮的时候,我们会延迟对 HUD 进行的输入操作。

MouseInterfacePlayerController.uc
class MouseInterfacePlayerController extends PlayerController;

// 鼠标事件枚举
enum EMouseEvent
{
  LeftMouseButton,
  RightMouseButton,
  MiddleMouseButton,
  ScrollWheelUp,
  ScrollWheelDown,
};

// 处理鼠标输入
function HandleMouseInput(EMouseEvent MouseEvent, EInputEvent InputEvent)
{
  local MouseInterfaceHUD MouseInterfaceHUD;

  // 类型转换获取我们的 HUD
  MouseInterfaceHUD = MouseInterfaceHUD(myHUD);

  if (MouseInterfaceHUD != None)
  {
    // 检测这个输入的类型
    if (InputEvent == IE_Pressed)
    {
      // 处理按下的事件
      switch (MouseEvent)
      {
        case LeftMouseButton:
     MouseInterfaceHUD.PendingLeftPressed = true;
     break;

   case RightMouseButton:
     MouseInterfaceHUD.PendingRightPressed = true;
     break;

   case MiddleMouseButton:
     MouseInterfaceHUD.PendingMiddlePressed = true;
     break;

   case ScrollWheelUp:
     MouseInterfaceHUD.PendingScrollUp = true;
     break;

   case ScrollWheelDown:
     MouseInterfaceHUD.PendingScrollDown = true;
     break;

   default:
     break;
      }
    }
    else if (InputEvent == IE_Released)
    {
      // 处理释放的事件
      switch (MouseEvent)
      {
        case LeftMouseButton:
     MouseInterfaceHUD.PendingLeftReleased = true;
     break;

   case RightMouseButton:
     MouseInterfaceHUD.PendingRightReleased = true;
     break;

   case MiddleMouseButton:
     MouseInterfaceHUD.PendingMiddleReleased = true;
     break;

   default:
     break;
      }
    }
  }
}

// 在按下鼠标左键和右键时使用的调用函数
exec function StartFire(optional byte FireModeNum)
{
  HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Pressed);
  Super.StartFire(FireModeNum);
}

// 在释放鼠标左键和右键时使用的调用函数
exec function StopFire(optional byte FireModeNum)
{
  HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Released);
  Super.StopFire(FireModeNum);
}

// 在按下鼠标中键时调用
exec function MiddleMousePressed()
{
  HandleMouseInput(MiddleMouseButton, IE_Pressed);
}

// 在释放鼠标中键时调用
exec function MiddleMouseReleased()
{
  HandleMouseInput(MiddleMouseButton, IE_Released);
}

// 在向上滚动鼠标中键滑轮时调用
exec function MiddleMouseScrollUp()
{
  HandleMouseInput(ScrollWheelUp, IE_Pressed);
}

// 在向下滚动鼠标中键滑轮时调用
exec function MiddleMouseScrollDown()
{
  HandleMouseInput(ScrollWheelDown, IE_Pressed);
}

//  清空这个函数
function UpdateRotation(float DeltaTime);

// 重新载入这个声明,因为在这个函数中没有对 StartFire 进行全局调用
auto state PlayerWaiting
{
  exec function StartFire(optional byte FireModeNum)
  {
    Global.StartFire(FireModeNum);
  }
}

defaultproperties
{
  // 将输入类设置为鼠标接口玩家输入
  InputClass=class'MouseInterfacePlayerInput'
}

玩家控制器后面的逻辑规则如下所示:

  • 通过按键绑定调用可执行函数。
  • 如果调用了任何可执行函数,那么它们会调用 HandleMouseInput,它随之会延迟对 HUD 进行的操作。

因为我们已经添加了新的可执行函数,所以我们需要修改 DefaultInput.ini,将其绑定给这些可执行函数。修改 DefaultInput.ini 可以确保后续的配置文件根据改动进行更新;不过您还可以修改 UDKInput.ini。

DefaultInput.ini
; 删除之前的鼠标向上滚动和向下滚动按键绑定
-Bindings=(Name="MouseScrollUp",Command="PrevWeapon")
-Bindings=(Name="MouseScrollDown",Command="NextWeapon")
; 添加鼠标中键按钮和新的滑轮按键绑定
.Bindings=(Name="MiddleMouseButton",Command="MiddleMousePressed | OnRelease MiddleMouseReleased")
.Bindings=(Name="MouseScrollUp",Command="GBA_PrevWeapon | MiddleMouseScrollUp")
.Bindings=(Name="MouseScrollDown",Command="GBA_NextWeapon | MiddleMouseScrollDown")

在鼠标输入延迟显示到 HUD 的情况下,HUD 会在 PostRender 函数中根据帧处理这些鼠标输入。在每一帧中,HUD 都会通过执行在世界中代表鼠标的轨迹获取当前鼠标交互接口。

进一步明确,在下面的图片中,绿色的平面代表世界,红色的椎体代表 2D 鼠标位置,而蓝色的线代表轨迹。椎体平坦的顶部代表屏幕(在这个实例中是从这个顶部观察世界)。画布反投射函数可以将一个 2D 位置转换为世界坐标。

FrustrumExplanation.jpg

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 这个纹理可以代表屏幕上的光标
var const Texture2D CursorTexture;
// 光标的颜色
var const Color CursorColor;
// 未处理的鼠标左键按下事件
var bool PendingLeftPressed;
// 未处理的鼠标左键释放事件
var bool PendingLeftReleased;
// 未处理的鼠标右键按下事件
var bool PendingRightPressed;
// 未处理的鼠标右键释放事件
var bool PendingRightReleased;
// 未处理的鼠标中键按下事件
var bool PendingMiddlePressed;
// 未处理的鼠标中键释放事件
var bool PendingMiddleReleased;
// 未处理的鼠标滑轮向上滚动事件
var bool PendingScrollUp;
// 未处理的鼠标滑轮向下滚动事件
var bool PendingScrollDown;
// 缓冲的鼠标世界坐标原点
var Vector CachedMouseWorldOrigin;
// 缓冲的鼠标世界坐标方向
var Vector CachedMouseWorldDirection;
// 最后的鼠标交互接口
var MouseInterfaceInteractionInterface LastMouseInteractionInterface;
// 使用 ScaleForm?
var bool UsingScaleForm;
// Scaleform 鼠标动画
var MouseInterfaceGFx MouseInterfaceGFx;

simulated event PostBeginPlay()
{
  Super.PostBeginPlay();

  // 如果我们使用的是 ScaleForm,那么创建 ScaleForm 动画
  if (UsingScaleForm)
  {
    MouseInterfaceGFx = new () class'MouseInterfaceGFx';
    if (MouseInterfaceGFx != None)
    {
      MouseInterfaceGFx.MouseInterfaceHUD = Self;
      MouseInterfaceGFx.SetTimingMode(TM_Game);

      MouseInterfaceGFx.Init(class'Engine'.static.GetEngine().GamePlayers[MouseInterfaceGFx.LocalPlayerOwnerIndex]);
    }
  }
}

simulated event Destroyed()
{
  Super.Destroyed();

  // 如果 ScaleForm 动画存在,那么销毁它
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.Close(true);
    MouseInterfaceGFx = None;
  }
}

function PreCalcValues()
{
  Super.PreCalcValues();

  // 如果 ScaleForm 动画存在,那么重新设置它的视窗、缩放模式和对齐方式,一以便与屏幕分辨率匹配
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.SetViewport(0, 0, SizeX, SizeY);
    MouseInterfaceGFx.SetViewScaleMode(SM_NoScale);
    MouseInterfaceGFx.SetAlignment(Align_TopLeft);
  }
}

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local Vector HitLocation, HitNormal;

  Super.PostRender();

  // 确保我们使用的不是 ScaleForm,同时我们有一个有效的光标
  if (!UsingScaleForm && CursorTexture != None)
  {
    // 确保我们有一个有效的 PlayerOwner
    if (PlayerOwner != None)
    {
      // 转换获取 MouseInterfacePlayerInput
      MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

      // 如果我们使用的不是 scale form,而且我们有一个有效的光标贴图,那么渲染它
      if (MouseInterfacePlayerInput != None)
      {
        // 将画布位置设置为鼠标位置
        Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
        // 设置光标颜色
        Canvas.DrawColor = CursorColor;
        // 绘制屏幕上的纹理
        Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
      }
    }
  }

  // 获取当前鼠标交互接口
  MouseInteractionInterface = GetMouseActor(HitLocation, HitNormal);

  // 处理鼠标选中及鼠标释放
  // 在这之前,我们有一个鼠标交互接口吗?
  if (LastMouseInteractionInterface != None)
  {
    // 如果最后的鼠标交互接口与当前的鼠标交互有差异
    if (LastMouseInteractionInterface != MouseInteractionInterface)
    {
      // 调用鼠标释放函数
      LastMouseInteractionInterface.MouseOut(CachedMouseWorldOrigin, CachedMouseWorldDirection);
      // 分配新的鼠标交互接口
      LastMouseInteractionInterface = MouseInteractionInterface;

      // 如果最后的鼠标交互接口不为空
      if (LastMouseInteractionInterface != None)
      {
        // 调用鼠标选中函数
        LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection); // Call mouse over
      }
    }
  }
  else if (MouseInteractionInterface != None)
  {
    // 分配新的鼠标交互接口
    LastMouseInteractionInterface = MouseInteractionInterface;
    // 调用鼠标选中函数
    LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  }

  if (LastMouseInteractionInterface != None)
  {
    // 处理鼠标左键
    if (PendingLeftPressed)
    {
      if (PendingLeftReleased)
      {
        // 这是一个左点击,所以删除
        PendingLeftPressed = false;
        PendingLeftReleased = false;
      }
      else
      {
        // 按下左键
        PendingLeftPressed = false;
        LastMouseInteractionInterface.MouseLeftPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingLeftReleased)
    {
      // 释放左键
      PendingLeftReleased = false;
      LastMouseInteractionInterface.MouseLeftReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 处理鼠标右键
    if (PendingRightPressed)
    {
      if (PendingRightReleased)
      {
        // 这是一个右点击,所以删除
        PendingRightPressed = false;
        PendingRightReleased = false;
      }
      else
      {
        // 按下右键
        PendingRightPressed = false;
        LastMouseInteractionInterface.MouseRightPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingRightReleased)
    {
      // 释放右键
      PendingRightReleased = false;
      LastMouseInteractionInterface.MouseRightReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 处理鼠标中键
    if (PendingMiddlePressed)
    {
      if (PendingMiddleReleased)
      {
        // 这是一个中键点击,所以删除
        PendingMiddlePressed = false;
        PendingMiddleReleased = false;
      }
      else
      {
        // 按下中键
        PendingMiddlePressed = false;
        LastMouseInteractionInterface.MouseMiddlePressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingMiddleReleased)
    {
      PendingMiddleReleased = false;
      LastMouseInteractionInterface.MouseMiddleReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 处理鼠标中键向上滚动
    if (PendingScrollUp)
    {
      PendingScrollUp = false;
      LastMouseInteractionInterface.MouseScrollUp(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 处理鼠标中键向下滚动
    if (PendingScrollDown)
    {
      PendingScrollDown = false;
      LastMouseInteractionInterface.MouseScrollDown(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }
  }
}

function MouseInterfaceInteractionInterface GetMouseActor(optional out Vector HitLocation, optional out Vector HitNormal)
{
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Actor HitActor;

  // 确保我们有一个有效的画布和玩家所有者
  if (Canvas == None || PlayerOwner == None)
  {
    return None;
  }

  // 类型转换获取新的玩家输入
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 确保玩家输入有效
  if (MouseInterfacePlayerInput == None)
  {
    return None;
  }

  // 我们将鼠标位置存储为 IntPoint,但是它需要存储为 Vector2D
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 反投射鼠标位置,并将其存储在缓冲的向量中
  Canvas.DeProject(MousePosition, CachedMouseWorldOrigin, CachedMouseWorldDirection);

  // 执行轨迹 actor 迭代函数。使用迭代函数,这样我们就可以得到最高层鼠标交互
  // 接口。这其中涵盖了其他可跟踪对象(例如,静态网格物体)位于鼠标交互
  // 接口之上的情况。
  ForEach TraceActors(class'Actor', HitActor, HitLocation, HitNormal, CachedMouseWorldOrigin + CachedMouseWorldDirection * 65536.f, CachedMouseWorldOrigin,,, TRACEFLAG_Bullet)
  {
    // 类型转换,查看 HitActor 是否可以实现鼠标交互接口
    MouseInteractionInterface = MouseInterfaceInteractionInterface(HitActor);
    if (MouseInteractionInterface != None)
    {
      return MouseInteractionInterface;
    }
  }

  return None;
}

function Vector GetMouseWorldLocation()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Vector MouseWorldOrigin, MouseWorldDirection, HitLocation, HitNormal;

  // 确保我们有一个有效的画布和玩家所有者
  if (Canvas == None || PlayerOwner == None)
  {
    return Vect(0, 0, 0);
  }

  // 类型转换获取新的玩家输入
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 确保玩家输入有效
  if (MouseInterfacePlayerInput == None)
  {
    return Vect(0, 0, 0);
  }

  // 我们将鼠标位置存储为 IntPoint,但是它需要存储为 Vector2D
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 反投射鼠标位置,并将其存储在缓冲的向量中
  Canvas.DeProject(MousePosition, MouseWorldOrigin, MouseWorldDirection);

  // 执行轨迹获取实际鼠标世界位置。
  Trace(HitLocation, HitNormal, MouseWorldOrigin + MouseWorldDirection * 65536.f, MouseWorldOrigin , true,,, TRACEFLAG_Bullet);
  return HitLocation;
}

defaultproperties
{
  // 如果您希望使用 Unreal 的玩家输入获取鼠标坐标,那么设置为 false
  UsingScaleForm=true
  CursorColor=(R=255,G=255,B=255,A=255)
  CursorTexture=Texture2D'EngineResources.Cursors.Arrow'
}

HUD 后面的逻辑规则如下所示:

  • 对于进行渲染的每一帧,我们都会渲染鼠标光标
  • 通过在世界坐标中执行轨迹获取当前鼠标交互接口。如果当前鼠标交互接口与最后一个交互接口有所不同,那么相应地调用鼠标选中或鼠标释放函数。
  • 如果我们有一个有效的鼠标交互接口,那么 HUD 会处理延迟的鼠标输入
  • 对于每一个鼠标输入,调用相应的接口函数并重新设置这个布尔变量

我只想获取鼠标 3D 坐标!

要获取基于鼠标 2D 坐标的鼠标 3D 坐标,您可以在您的 HUD 类中使用这个函数。需要 HUD 类,因为需要 Deproject 函数。

MouseInterfaceHUD.uc
function Vector GetMouseWorldLocation()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Vector MouseWorldOrigin, MouseWorldDirection, HitLocation, HitNormal;

  // 确保我们有一个有效的画布和玩家所有者
  if (Canvas == None || PlayerOwner == None)
  {
    return Vect(0, 0, 0);
  }

  // 类型转换获取新的玩家输入
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 确保玩家输入有效
  if (MouseInterfacePlayerInput == None)
  {
    return Vect(0, 0, 0);
  }

  // 我们将鼠标位置存储为 IntPoint,但是它需要存储为 Vector2D
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 反投射鼠标位置,并将其存储在缓冲的向量中
  Canvas.DeProject(MousePosition, MouseWorldOrigin, MouseWorldDirection);

  // 执行轨迹获取实际鼠标世界位置。
  Trace(HitLocation, HitNormal, MouseWorldOrigin + MouseWorldDirection * 65536.f, MouseWorldOrigin , true,,, TRACEFLAG_Bullet);
  return HitLocation;
}

相关主题

添加 Kismet 鼠标输入事件


这是自定义 Kismet 事件,它可以使 Kismet 检测到该玩家与 actor 进行交互的具体时间。

SeqEvent_MouseInput.uc
class SeqEvent_MouseInput extends SequenceEvent;

var Vector HitLocation;
var Vector HitNormal;
var Vector MouseWorldOrigin;
var Vector MouseWorldDirection;

event Activated()
{
  local MouseInterfaceInteractionInterface MouseInteractionInterface;

  // 类型转换起源以确保它是一个鼠标交互接口
  MouseInteractionInterface = MouseInterfaceInteractionInterface(Originator);

  if (MouseInteractionInterface != None)
  {
    // 获取相应的值,这样我们才可以在激活事件的时候使它们出界
    MouseWorldOrigin = MouseInteractionInterface.GetMouseWorldOrigin();
    MouseWorldDirection = MouseInteractionInterface.GetMouseWorldDirection();
    HitLocation = MouseInteractionInterface.GetHitLocation();
    HitNormal = MouseInteractionInterface.GetHitNormal();
  }
}

defaultproperties
{
  ObjName="Mouse Input"
  ObjCategory="Input"

  bPlayerOnly=false
  MaxTriggerCount=0

  OutputLinks(0)=(LinkDesc="Left Pressed")
  OutputLinks(1)=(LinkDesc="Left Released")
  OutputLinks(2)=(LinkDesc="Right Pressed")
  OutputLinks(3)=(LinkDesc="Right Released")
  OutputLinks(4)=(LinkDesc="Middle Pressed")
  OutputLinks(5)=(LinkDesc="Middle Released")
  OutputLinks(6)=(LinkDesc="Scroll Up")
  OutputLinks(7)=(LinkDesc="Scroll Down")
  OutputLinks(8)=(LinkDesc="Mouse Over")
  OutputLinks(9)=(LinkDesc="Mouse Out")

  VariableLinks(1)=(ExpectedType=class'SeqVar_Vector',LinkDesc="HitLocation",bWriteable=true,PropertyName=HitLocation)
  VariableLinks(2)=(ExpectedType=class'SeqVar_Vector',LinkDesc="HitNormal",bWriteable=true,PropertyName=HitNormal)
  VariableLinks(3)=(ExpectedType=class'SeqVar_Vector',LinkDesc="MouseWorldOrigin",bWriteable=true,PropertyName=MouseWorldOrigin)
  VariableLinks(4)=(ExpectedType=class'SeqVar_Vector',LinkDesc="MouseWorldDirection",bWriteable=true,PropertyName=MouseWorldDirection)
}

在 Kismet 鼠标输入序列中没有多少逻辑规则。事实上,这个 Kismet 事件可以作为 Unrealscript 和 Kismet 之间的网关。它还可以输出一些通过 HUD 计算的值。Actor 应该实现这个交互接口,并将这个 Kismet 节点放置在 SupportedEvent 数组中。

相关主题

以添加 KActor 子类为例


这是一个有关鼠标交互接口以及 Kismet 鼠标输入事件节点的使用情况示例。

实现鼠标交互接口后,这个鼠标接口 HUD 即可使用它。它可以适用于诸如 Pawn、Projectile 或 Vehicle 的大量类。

通过支持鼠标输入 Kismet 序列事件,关卡设计师可以利用这段代码创建鼠标交互,不需要亲自写代码。

MouseInterfaceKActor.uc
class MouseInterfaceKActor extends KActor
  Implements(MouseInterfaceInteractionInterface);

var Vector CachedMouseHitLocation;
var Vector CachedMouseHitNormal;
var Vector CachedMouseWorldOrigin;
var Vector CachedMouseWorldDirection;

// ===
// MouseInterfaceInteractionInterface implementation
// ===
function MouseLeftPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 0);
}

function MouseLeftReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 1);
}

function MouseRightPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 2);
}

function MouseRightReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 3);
}

function MouseMiddlePressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 4);
}

function MouseMiddleReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 5);
}

function MouseScrollUp(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 6);
}

function MouseScrollDown(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 7);
}

function MouseOver(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 8);
}

function MouseOut(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 9);
}

function Vector GetHitLocation()
{
  return CachedMouseHitLocation;
}

function Vector GetHitNormal()
{
  return CachedMouseHitNormal;
}

function Vector GetMouseWorldOrigin()
{
  return CachedMouseWorldOrigin;
}

function Vector GetMouseWorldDirection()
{
  return CachedMouseWorldDirection;
}

defaultproperties
{
  SupportedEvents(4)=class'SeqEvent_MouseInput'
}

MouseInterfaceKActor 后面的逻辑规则如下所示:

  • 在通过 HUD 执行任何接口实现函数时,我们会触发附加的 Mouse Input Kismet(鼠标输入 Kismet)节点。在触发器调用结尾的数字是输出索引。

使用 Kismet 鼠标输入事件


创建一个新的地图,打开 WorldInfo 属性。要进行这项操作,请点击“视图”菜单,然后点击“World Info 属性”。展开 "World Info" 选项卡。将 PIE 的 GameType 设置为 MouseInterfaceGameInfo。默认情况下,它将会使用 UTDeathmatch。

Kismet_00_AdjustWorldInfo.jpg

展开 "Lightmass" 选项卡并取消勾选“使用全局光照”复选框。在测试地图中不需要使用 light mass。

Kismet_01_AdjustWorldInfoLightmass.jpg

打开“内容浏览器”并选择“Actor 类”选项卡。在这里展开这个类的树形列表,选择 "MouseInterfaceKActor"。这样当您在世界视图中打开关联菜单的时候,可以将它添加到世界中。

Kismet_02_SelectMouseInterfaceKActor.jpg

在世界视图中右击打开视图关联菜单。选择并点击“在此处添加 MouseInterfaceKActor”添加一个 MouseInterfaceKActor。

Kismet_03_AddMouseInterfaceKActor.jpg

因为 MouseInterfaceKActor 没有一个可见的静态网格物体,所以它将会首先作为选中的变换控件(选择、运动、旋转或缩放控件)显示。按下 F4 打开 actor 属性窗口。点击顶部的挂锁图标对选项进行设置。

Kismet_04_OpenTheKActorProperties.jpg

在“内容浏览器”中查找并选择一个感兴趣的静态网格物体。在“内容浏览器”中选择它。

Kismet_05_SelectAPhysicsStaticMesh.jpg

展开“动态 SMActor”选项卡,展开“静态网格物体组件”对象,然后最后展开“静态网格物体组件”选项卡。按下“静态网格物体”字段旁边的绿色箭头。它将会设置您在“内容浏览器”中选择的静态网格物体。您应该会看到世界视图中出现的圆筒。

Kismet_06_SetTheStaticMesh.jpg

在关卡中添加所需的光照,然后编译这个关卡。

Kismet_07_AddADominantDirectionalLight.jpg

通过在编辑器工具栏中按下 Kismet_08_OpenKismet.jpg 打开 Kismet 窗口。

在世界视图中仍然选中 MouseInterfaceKActor,右击 Kismet 窗口中的空白区域调出关联菜单。添加我们的新 Mouse Input Event(鼠标输入事件)。这个方法可以确保将我们的事件添加到位于世界中的 MouseInterfaceKActor。

Kismet_09_CreateNewMouseInputEvent.jpg

这是我们的 Mouse Input event(鼠标输入事件)。正如您所见,所有输出链接都与可能出现的鼠标输入类型有关。该事件还可以输出我们也可以使用的各种变量。

Kismet_10_TheMouseInputEvent.jpg

为了测试我们的 Mouse Input 事件,让我们创建一些记录操作。

Kismet_11_AddLogAction.jpg

设置记录操作,输出一些适用于我们想要测试的事件输出的文本。

Kismet_12_SetLogAction.jpg

创建更多的记录操作,测试所有的事件输出。将它们对应地连接起来。

Kismet_13_DuplicateAndSetLogActions.jpg

在 PIE 中运行这个关卡,同时您应该可以测试鼠标输入 Kismet 接口。记住,鼠标按钮和滚动操作只有在鼠标选中 MouseInterfaceKActor 后才会反应灵敏。

Kismet_14_Test.jpg

让我们通过创建一个允许我们射中 MouseInterfaceKActor 的序列进一步阐述这个示例。首先创建一个新的向量变量。这样我们就可以存储鼠标原点和鼠标点击位置。

Kismet_15_AddVectorVariable.jpg

将向量变量绑定到事件的变量输出,即 "HitLocation" 和 "Mouse WorldOrigin"。

Kismet_16_BindVectorToEvent.jpg

添加一个新的“生成射弹”操作。它可以在执行这项操作的时候创建一个射弹。

Kismet_17_CreateSpawnProjectileAction.jpg

这是新建的“生成射弹”操作。

Kismet_18_SpawnProjectileAction.jpg

将“生成射弹”操作连接到在按下鼠标左键后激活的“记录”操作。将“鼠标 WorldOrigin”变量连接到与“生成射弹”事件相关的“生成位置”输入数据。将 "HitLocation" 变量连接到与“生成射弹”事件相关的“目标位置”输入数据。这样将会使“生成射弹”在朝向鼠标点击位置移动的鼠标世界原点位置生成一个射弹。因此看上去好像是我们在世界中射中了 MouseInterfaceKActor。

Kismet_19_ConnectSpawnProjectileAction.jpg

设置要生成的射弹类型。

Kismet_20_SetSpawnProjectileAction.jpg

添加 "All Players" 变量。这是“生成射弹”操作需要的输入数据。

Kismet_21_AddPlayerVariable.jpg

这里是新建的 "All Players" 变量。

Kismet_22_PlayerVariable.jpg

将 "All Players" 变量连接到 "Spawn Projectile"。

Kismet_23_ConnectPlayerVariable.jpg

运行 PIE 并测试 Kismet。您现在应该可以射中 MouseInterfaceKActor。

Kismet_24_Test.jpg

相关主题

下载


  • 下载该精华文章中使用的内容。(MouseInterface.zip)