This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
专业(SIM)
UnigineEditor
界面概述
资源工作流程
版本控制
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
使用范例
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
材质和着色器
Rebuilding the Engine Tools
GUI
双精度坐标
应用程序接口
Animations-Related Classes
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
VR-Related Classes
创建内容
内容优化
材质
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials

实施射击

Now, we can set up the shooting ability and prepare effects for it. When a player presses the Left Mouse Button, the robot fires a bullet from one of its guns. Since there are two guns, we can alternate the fire. 现在,我们可以设置射击能力并为其准备效果。 当玩家按下鼠标左键时,机器人会从它的一把枪中发射子弹。 由于有两支枪,我们可以交替开火。

Step 1. Create, Move, and Delete a Bullet
步骤1。创建、移动和删除一颗子弹#

We need to set up a node to represent a bullet in the game. The bullet will fly in the specified direction and explode on impact with an object. If it doesn't hit anything and the time runs out, it will be destroyed. Upon an impact with any dynamic object within the Play Area, an impulse will be applied to its physical body.我们需要建立一个节点代表一颗子弹在游戏中。子弹飞在指定的方向和爆炸影响的对象。如果没有击中任何和耗尽的时候,它将被摧毁。在影响与任何游戏区域中的动态对象,一个脉冲将被应用到它的身体。

We will use the bit masking mechanism to identify the objects that can be hit by a bullet. A bullet checks for an intersection between its trajectory and surfaces of other objects with the BulletIntersection bit (described below) enabled in an Intersection mask. Our bullets will hit the walls and explode with a hit effect. The effect consists of a light flash, a cracks decal, a blaster sound, and sparks particles. The effect loads and plays at the hit position.我们将使用 位掩码 机制来识别可以被子弹击中的对象。 子弹通过 Intersection 掩码中启用的 BulletIntersection 位(如下所述)检查其轨迹和其他对象的曲面之间的相交。 我们的子弹会击中墙壁并以命中效果爆炸。 效果包括 light 闪光、裂缝 贴花、爆破器 声音,并激发粒子。 效果在命中位置加载和播放。

Every node has a transformation matrix, which encodes position, rotation, and scale of the node in the world. There are different ways to perform basic node transformations. We will calculate a new position for the bullet's trajectory each frame based on the time it took to render the last game frame. This way we will make sure its speed is the same (frame-rate independent) no matter how often the Update method is called by the player's hardware.每个节点都有一个变换矩阵,它编码了节点在世界中的位置、旋转和比例。 有不同的方式来执行基本节点 transformations。 我们将根据渲染最后一帧游戏所花费的时间为每一帧计算子弹轨迹的新位置。 这样,无论播放器硬件调用 Update 方法的频率如何,我们都将确保其速度相同(与帧速率无关)。

  1. For every wall's box surface enable the 7th bit of the Intersection Mask, and call it BulletIntersection.对于每一个墙的box曲面启用Intersection位掩码的第七位,并称之为BulletIntersection

  2. Create a new C# component and call it Projectile. Open your IDE and copy the following code. Save your code in the IDE to ensure it's automatic compilation on switching back to UnigineEditor.创建一个新的 C# 组件 并将其命名为 Projectile。 打开您的 IDE 并复制以下代码。 将您的代码保存在 IDE 中,以确保它在切换回 UnigineEditor 时自动编译。

    Projectile.cs
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unigine;
    
    #if UNIGINE_DOUBLE
    using Vec3 = Unigine.dvec3;
    using Mat4 = Unigine.dmat4;
    using Scalar = System.Double;
    #else
    	using Vec3 = Unigine.vec3;
    	using Mat4 = Unigine.mat4;
    	using Scalar = System.Single;
    #endif 
    [Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
    public class Projectile : Component
    {
    	// the speed of bullet movement
    	public float speed = 30.0f;
    
    	// asset file that contains the hit effect
    	public AssetLinkNode bulletHitEffect;
    
    	static WorldIntersectionNormal intersection;
    
    	void Init()
    	{
    		if (intersection == null)
    			intersection = new WorldIntersectionNormal();
    	}
    	
    	void Update()
    	{
    		Vec3 oldPos = node.WorldPosition;
    		vec3 dir = node.GetWorldDirection(MathLib.AXIS.Y);
    
    		// calculate the next position of the bullet
    		Vec3 newPos = oldPos + dir * speed * Game.IFps;
    		
    		// check intersection with other objects
    		Unigine.Object obj = World.GetIntersection(oldPos, newPos, ~0, intersection); //all mask bits are set to 1
    		if (obj)
    		{
    			// spawn a bullet at the hit point
    			Node hitEffect = bulletHitEffect.Load(intersection.Point);
    			// orient the effect towards the hit direction 
    			hitEffect.SetWorldDirection(intersection.Normal, vec3.UP, MathLib.AXIS.Y);
    
    			// add impulse to an object if it is a body rigid 
    			BodyRigid rb = obj.BodyRigid;
    			if (rb != null)
    			{
    				rb.Frozen = false;
    				rb.AddWorldImpulse(obj.WorldPosition, node.GetWorldDirection(MathLib.AXIS.Y) * speed);
    			}
    
    			// remove the bullet
    			node.DeleteLater();
    
    			return;
    		}
    		else
    		{
    			// move the bullet to a new position
    			node.WorldPosition = newPos;
    		}
    	}
    }
  3. As the Life Time of the bullet runs out we should delete it by simply calling the DeleteLater() method. Create a new C# component and call it Destroy. Open your IDE, add the following code, save and switch to UnigineEditor to compile it.随着子弹的生命周期用完,我们应该通过简单地调用 DeleteLater() 方法来删除它。 创建一个新的 C# 组件 并将其命名为 Destroy。 打开您的IDE,添加以下代码,保存并切换到UnigineEditor进行编译。

    Destroy.cs
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unigine;
    
    [Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
    public class Destroy : Component
    {
    	[ShowInEditor][Parameter(Tooltip = "Object's lifetime")]
    	private float lifeTime = 1.0f;
    
    	private float startTime = 0.0f;
    
    	void Init()
    	{
    		// remember initialization time of an object
    		startTime = Game.Time;
    	}
    
    	void Update()
    	{
    		// wait until the lifetime ends and delete the object
    		if (Game.Time - startTime > lifeTime)
    			node.DeleteLater();
    	}
    }
  4. Drag programming_quick_start\character\bullet\bullet.node from the Asset Browser into the Viewport and click Edit in the Reference section of the Parameters window to make modifications. Add a Destroy component to the child ObjectMeshStatic bullet node and set the Life Time to 5.0.programming_quick_start\character\bullet\bullet.node 节点从 Asset Browser 拖到 Viewport 并单击 Parameters 窗口的 Reference 部分中的 Edit 进行修改。 将 Destroy 组件添加到 ObjectMeshStatic bullet 子节点并设置 Life Time5.0

  5. Next, drag the programming_quick_start\character\bullet_hit\bullet_hit.node from the Asset Browser into the Viewport, click Edit on the Parameters window, add the Destroy component to the bullet_hit's NodeDummy and to the LightOmni. Set Life Time values to 10.0 and 0.05 seconds respectively.接下来,将 Asset Browser 中的 programming_quick_start\character\bullet_hit\bullet_hit.node 拖到 Viewport 中,点击 EditParameters 窗口中,将 Destroy 组件添加到 bullet_hitNodeDummyLightOmni。 将 Life Time 值分别设置为 10.00.05 秒。

  6. Then select the bullet_hit Node Reference and click Apply in the Reference section to save all changes made to it.然后选择 bullet_hit Node Reference 并 在 Reference 部分中点击 Apply 保存对其所做的所有更改。
  7. Then, add a Projectile component to the child ObjectMeshStatic bullet of the bullet Node Reference. Assign the bullet_hit node from the Asset Browser window to the Bullet Hit Effect field.然后,添加一个Projectile组件的子ObjectMeshStatic bullet bullet Node Reference。将bullet_hit节点从Asset Browser窗口分配给Bullet Hit Effect字段。

  8. Now disable Intersection detection for the bullet to avoid the bullet detecting intersections with itself. Select the bullet_mat surface and uncheck the Intersection option for it.现在禁用子弹的碰撞测试避免子弹与自身检测十字路口。选择bullet_mat曲面和取消Intersection选项。
  9. Save changes to the bullet Node Reference, by selecting it and clicking Apply in the Reference section or simply press Ctrl+S hotkey to save all changed assets.将更改保存到 bullet Node Reference,方法是选择它并单击 应用Reference 部分,或简单地按 Ctrl+S 热键来保存所有更改的资源。
  10. Now you can delete bullet and bullet_hit nodes from the world as we will spawn them via code.现在您可以从世界中删除bulletbullet_hit,因为我们将通过代码生成它们。

Step 2. Spawn a Bullet
步骤2。生成子弹#

Let's create special spawn nodes using Dummy Nodes with no visual representation. Their positions will be used as initial bullet positions.让我们使用 Dummy Node创建特殊刷出节点,但不使用视觉表示。它们的位置将被用作初始子弹的位置。

  1. Select the robot Node Reference in the World Nodes window and click Edit in the Reference section of the Parameters window.World Nodes 窗口中选择 robot Node Reference 并在 Parameters 窗口的 Reference 部分中点击 Edit
  2. Right-click on the child robot ObjectMeshSkinned in the World Nodes window to add a child node. Choose Create->Node->Dummy and position it in the Viewport near the end of the left gun. The Y axis (green arrow) must point in the fire direction, since Unigine uses the right-handed Cartesian coordinate system.World Nodes 窗口中右键单击子 robot ObjectMeshSkinned 以添加子节点。 选择 Create->Node->Dummy 并将其放置在 Viewport 靠近左枪末端的位置。 Y 轴(绿色箭头)必须指向射击方向,因为 UNIGINE 使用 右手 笛卡尔坐标系。
  3. Rename the Dummy Node as "left_bullet_spawn".Dummy Node 重命名为 "left_bullet_spawn"

  4. Create a spawn point for the right gun the same way and call it "right_bullet_spawn".以同样的方式为右枪创建一个刷出点,并将其命名为 "right_bullet_spawn"
  5. To spawn bullets at run-time via API on Right Mouse Button click, add the following code to the PlayerController component. We use AssetLinkNode to refer to the bullet node file. Save your code in an IDE to ensure it's automatic recompilation on switching back to UnigineEditor.要在运行时通过 API 在单击鼠标右键时生成子弹,请将以下代码添加到 PlayerController 组件。 我们使用 AssetLinkNode 来引用子弹节点文件。 将您的代码保存在 IDE 中,以确保在切换回 UnigineEditor 时自动重新编译。

    PlayerController.cs
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using Unigine;
    
    #if UNIGINE_DOUBLE
    using Vec3 = Unigine.dvec3;
    using Mat4 = Unigine.dmat4;
    using Scalar = System.Double;
    #else
    	using Vec3 = Unigine.vec3;
    	using Mat4 = Unigine.mat4;
    	using Scalar = System.Single;
    #endif 
    [Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
    public class PlayerController : Component
    {
    //========================== NEW - BEGIN ===============================
    	bool isNextLeft = false;
    
    	// mouse fire button 
    	public Input.MOUSE_BUTTON mouseFireKey = Input.MOUSE_BUTTON.RIGHT;
    
    	// asset file that contains the bullet
    	public AssetLinkNode bullet;
    
    	public Node leftSpawn, rightSpawn;
    //========================== NEW - END ===============================
    	Player player;
    
    	BodyRigid rigid;
    
    	Vec3 pos;
    
    	//a WorldIntersection object to store the information about the intersection
    	WorldIntersection intersection = new WorldIntersection();
    
    	void Init()
    	{
    		player = Game.Player; 
    
    		rigid = node.ObjectBodyRigid;
    		rigid.AngularScale = new vec3(0.0f, 0.0f, 0.0f); // restricting the rotation
    		rigid.LinearScale = new vec3(1.0f, 1.0f, 0.0f); // restricting Z movement
    		rigid.MaxLinearVelocity = 8.0f; // clamping the max linear velocity
    	}
    //========================== NEW - BEGIN ===============================
    	void Update() 
    	{
    		if (Input.IsMouseButtonDown(mouseFireKey) && bullet.IsFileExist)
    		{
    			// load the bullet and set its position 
    			if (isNextLeft)
    				bullet.Load(rightSpawn.WorldTransform);
    			else
    				bullet.Load(leftSpawn.WorldTransform);
    
    			// alternate between the left and the right gun
    			isNextLeft = !isNextLeft;
    		}
    
    		// press ESC button to close the game
    		if (Input.IsKeyDown(Input.KEY.ESC))
    		{
    			Engine.Quit();
    		}
    	}
    //========================== NEW - END ===============================
    	void UpdatePhysics() 
    	{
    		// forward
    		if (Input.IsKeyPressed(Input.KEY.W))
    			Move(player.GetWorldDirection(MathLib.AXIS.Y)); 
    
    		// backward
    		if (Input.IsKeyPressed(Input.KEY.S))
    			Move(player.GetWorldDirection(MathLib.AXIS.NY)); 
    		// left
    		if (Input.IsKeyPressed(Input.KEY.A))
    			Move(player.GetWorldDirection(MathLib.AXIS.NX)); 
    
    		// right
    		if (Input.IsKeyPressed(Input.KEY.D))
    			Move(player.GetWorldDirection(MathLib.AXIS.X)); 
    
    		// finding the positions of the cursor and the point moved 100 units away in the camera forward direction 
    		ivec2 mouse = Input.MousePosition;
    		Vec3 p0 = player.WorldPosition;
    		Vec3 p1 = p0 + new Vec3(player.GetDirectionFromMainWindow(mouse.x, mouse.y)) * 100; 
    
    		// casting a ray from p0 to p1 to find the first intersected object
    		Unigine.Object obj = World.GetIntersection(p0, p1, 1, intersection); // the first bit of the intersection mask is set to 1, the rest are 0s
    
    		// finding the intersection position, creating a transformation matrix to face this position and setting the transofrm matrix for the body preserving angular and linear velocities
    		if (obj)
    		{
    			pos = intersection.Point;
    			pos.z = rigid.Transform.Translate.z; // project the position vector to the Body Rigid pivot plane
    			Mat4 transform = MathLib.SetTo(rigid.Transform.Translate, pos, vec3.UP, MathLib.AXIS.Y);
    			rigid.SetPreserveTransform(transform);
    		}
    	}
    
    	// moving the rigid body with linear impulse in the specified direction
    	void Move(vec3 direction) 
    	{
    		//directon is a normalized camera axis vector 
    		rigid.AddLinearImpulse(direction);
    	}
    }
  6. Drag left_bullet_spawn and right_bullet_spawn nodes to the corresponding fields of the PlayerController component of the robot node (ObjectMeshSkinned). And assign the bullet.node to the Bullet Path field.left_bullet_spawnright_bullet_spawn 节点拖到相应的字段 PlayerController 组成部分robot 节点(ObjectMeshSkinned)。将 bullet.node 分配给 Bullet Path字段。

  7. Save changes to the world, go to File->Save World or press Ctrl+S hotkey.拯救世界的变化,去File->Save World或者按Ctrl+S热键。
最新更新: 2024-04-19
Build: ()