当前位置: 首页 > news >正文

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十六集(上篇):制作更多地图,更多敌人,更多可交互对象

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、第一个代表性场景
    • 1.制作敌人僵尸跳跳虫更多敌人
    • 2.制作敌人阿斯匹德更多可交互对象
    • 3.制作敌人孵化虫和它的孩子
  • 二、第二个代表性场景
    • 1.制作更多敌人
    • 2.制作更多可交互对象
  • 总结


前言

         hello大家好久没见,之所以隔了这么久才更新并不是因为我又放弃了这个项目,而是接下来要制作的工作太忙碌了,每次我都花了很长的时间解决完一个部分,然后就没力气打开CSDN写文章就直接睡觉去了,现在终于有时间整理下我这半个月都做了什么内容。

        废话少说,接下来我将继续介绍我做的几个代表性场景,,可能你会说:“怎么标题还和上期一模一样”,那当然是没办法的事情,毕竟一期讲完这么多场景,敌人,可交互对象你没看睡着我都快写睡着了,OK我们接着来制作更多地图,更多敌人,更多可交互对象

        另外,我的Github已经更新了,想要查看最新的内容话请到我的Github主页下载工程吧:

GitHub - ForestDango/Hollow-Knight-Demo: A new Hollow Knight Demo after 2 years!


一、第一个代表性场景

1.制作敌人僵尸跳跳虫更多敌人

我突然找到一个有四个新类型敌人的场景,让我们先来介绍这四少吧,但首先还是先搭建好场景

可能看到这里你还不知道这对应的是游戏里的哪个场景,但只要我加上场景景色一切将豁然开朗

往上走去鹿角站,往右走去打苍蝇之母因此记得添加好对应的TransitionPoint。

我们先来制作最简单的僵尸跳跳虫:做好相对应的tk2dsprite和tk2dspriteAnimator。

 

添加相对应的脚本:

 

 老朋友Attack Range攻击距离:

落地状态下的灰尘粒子系统Dust:

创建一个名字为“Zombie Swipe”的playmakerFSM,然后老规矩贴出事件和变量:

逐个讲状态:

初始化阶段:

准备阶段:逐帧判断攻击距离和可视范围内:

判断玩家位置:

攻击准备阶段:

起飞阶段 

落地阶段:

冷却阶段:

回到空闲阶段:

重置Walker脚本的行为:

        这样一个简单跳跳僵尸虫就完成了。

2.制作敌人阿斯匹德更多可交互对象

        然后是制作敌人阿斯匹德,注意,这个不是折磨玩家的原始阿斯匹德,而是只会喷一个方向的:

首先添加好tk2dsprite和tk2dSpriteAnimator:

长开火和短开火的动画:

添加好相应的脚本:

除此之外,我们还要制作攻击用的子弹

我们先来制作玩家离开攻击距离的playmakerFSM:

这里会设置Spitter的playmakerFSM里面的bool变量unalert Range        以及设置bool变量Can See Hero

 

 

回到阿斯匹德spitter中,我们首先创建一个之前讲过的playmakerFSM叫flyer_receive_direction_msg。这个我之前讲蚊子那期用过,就是接收不同方向信息的,

然后才是我们的重点“spitter” FSM:

初始化状态:播放音效和动画,确认初始时的朝向,空闲状态漫无目的的IdleBuzz,通过Alert Range New来判断是否在攻击距离里,并check Can See Hero.

如果满足上述两个条件直接进入Alert状态: 

 

径直向玩家飞去:

判断位置并射线检测:

 

再飞行一小段距离,朝向玩家:

准备开火阶段:

设置发射的子弹朝向玩家,设置好速度

当玩家离开攻击距离后,回到Unalert Frame状态,下一帧再回到Idle状态

 这里我们来创建子弹预制体:

新建一个脚本名字叫EnemyBullet.cs
 

using System.Collections;
using UnityEngine;[RequireComponent(typeof(Rigidbody2D))]
public class EnemyBullet : MonoBehaviour
{public float scaleMin = 1.15f;public float scaleMax = 1.45f;private float scale;[Space]public float stretchFactor = 1.2f;public float stretchMinX = 0.75f;public float stretchMaxY = 1.75f;[Space]public AudioSource audioSourcePrefab;public AudioEvent impactSound;private bool active;private Rigidbody2D body;private tk2dSpriteAnimator anim;private Collider2D col;private void Awake(){body = GetComponent<Rigidbody2D>();anim = GetComponent<tk2dSpriteAnimator>();col = GetComponent<Collider2D>();}private void OnEnable(){active = true;scale = Random.Range(scaleMin, scaleMax);col.enabled = true;body.isKinematic = false;body.velocity = Vector2.zero;body.angularVelocity = 0f;anim.Play("Idle");}private void Update(){if (active){float rotation = Random.Range(body.velocity.y,body.velocity.x) * 57.295776f;transform.SetRotation2D(rotation);float num = 1f - body.velocity.magnitude * stretchFactor * 0.01f;float num2 = 1f + body.velocity.magnitude * stretchFactor * 0.01f;if (num2 < stretchMinX){num2 = stretchMinX;}if (num > stretchMaxY){num = stretchMaxY;}num *= scale;num2 *= scale;transform.localScale = new Vector3(num2, num, transform.localScale.z);}}private void OnCollisionEnter2D(Collision2D collision){if (active){active = false;StartCoroutine(Collision(collision.GetSafeContact().Normal, true));}}private void OnTriggerEnter2D(Collider2D collision){if(active && collision.tag == "HeroBox"){active = false;StartCoroutine(Collision(Vector2.zero, false));}}public void OrbitShieldHit(Transform shield){if (active){active = false;Vector2 normal = transform.position - shield.position;normal.Normalize();StartCoroutine(Collision(normal, true));}}private IEnumerator Collision(Vector2 normal, bool doRotation){transform.localScale = new Vector3(scale, scale, transform.localScale.z);body.isKinematic = true;body.velocity = Vector2.zero;body.angularVelocity = 0f;tk2dSpriteAnimationClip impactAnim = anim.GetClipByName("Impact");anim.Play(impactAnim);if (!doRotation || (normal.y >= 0.75f && Mathf.Abs(normal.x) < 0.5f)){transform.SetRotation2D(0f);}else if (normal.y <= 0.75f && Mathf.Abs(normal.x) < 0.5f){transform.SetRotation2D(180f);}else if (normal.x >= 0.75f && Mathf.Abs(normal.y) < 0.5f){transform.SetRotation2D(270f);}else if (normal.x <= 0.75f && Mathf.Abs(normal.y) < 0.5f){transform.SetRotation2D(90f);}impactSound.SpawnAndPlayOneShot(audioSourcePrefab, transform.position);yield return null;col.enabled = false;yield return new WaitForSeconds((impactAnim.frames.Length - 1) / impactAnim.fps);Destroy(gameObject);//TODO:}}

回到Unity编辑器添加好对应的参数,

最后再添加一个子对象,一个小小的亮灯:

3.制作敌人孵化虫和它的孩子

这个敌人我忘了叫什么名字了,暂且叫它孵化虫吧,先添加上tk2dsprite和tk2dspriteanimator:

它的攻击方式只有一种,那就是生下小虫子,然后小虫子来攻击玩家,子对象只需要一个Alert Range New

上面的playmakerFSM除了之前讲过的flyer_receive_direction_msg,还有就是“Hatcher”

这里给它最多生五个孩子

在开始前,我们可以在场景中偏离核心区域的地方预生成它的孩子,并把它们关在笼子里面别到处乱跑,

注意添加好tag,这里我先预生成了15只虫子宝宝并让它们待在这个隐形笼子里面。 

 

如上面阿斯匹德一样Idle状态:

 

 

 检查是否已经生够了到达最大可生成数量:

 来点生下来的音效,设置好生下来的位置,为宝宝的playmakerFSM发送事件SPAWN:

 

 这里有几个自定义playmakerFSM行为脚本我好像忘记说了:

using System;
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Audio)][Tooltip("Instantiate an Audio Player object and play a oneshot sound via its Audio Source.")]public class AudioPlayerOneShot : FsmStateAction{[RequiredField][CheckForComponent(typeof(AudioSource))][Tooltip("The object to spawn. Select Audio Player prefab.")]public FsmGameObject audioPlayer;[RequiredField][Tooltip("Object to use as the spawn point of Audio Player")]public FsmGameObject spawnPoint;[CompoundArray("Audio Clips", "Audio Clip", "Weight")]public AudioClip[] audioClips;[HasFloatSlider(0f, 1f)]public FsmFloat[] weights;public FsmFloat pitchMin;public FsmFloat pitchMax;public FsmFloat volume;public FsmFloat delay;public FsmGameObject storePlayer;private AudioSource audio;private float timer;public override void Reset(){spawnPoint = null;audioClips = new AudioClip[3];weights = new FsmFloat[]{1f,1f,1f};pitchMin = 1f;pitchMax = 1f;volume = 1f;timer = 0f;}public override void OnEnter(){timer = 0f;if(delay.Value == 0f){DoPlayRandomClip();Finish();}}public override void OnUpdate(){if(delay.Value > 0f){if(timer < delay.Value){timer += Time.deltaTime;return;}DoPlayRandomClip();Finish();}	}private void DoPlayRandomClip(){if (audioClips.Length == 0)return;GameObject value = audioPlayer.Value;Vector3 position = spawnPoint.Value.transform.position;Vector3 up = Vector3.up;//TODO:这行记得要改,因为我还没做对象池GameObject gameObject = UnityEngine.Object.Instantiate(audioPlayer.Value, position, Quaternion.Euler(up));audio = gameObject.GetComponent<AudioSource>();int randomWeightIndex = ActionHelpers.GetRandomWeightedIndex(weights);if(randomWeightIndex != -1){AudioClip audioClip = audioClips[randomWeightIndex];if(audioClip != null){float pitch = UnityEngine.Random.Range(pitchMin.Value, pitchMax.Value);audio.pitch = pitch;audio.PlayOneShot(audioClip);}}audio.volume = volume.Value;}public AudioPlayerOneShot(){pitchMin = 1f;pitchMax = 2f;}}}
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.GameObject)][Tooltip("Spawns a random amount of chosen GameObject from global pool and fires them off in random directions.")]public class FlingObjectsFromGlobalPool : RigidBody2dActionBase{[RequiredField][Tooltip("GameObject to spawn.")]public FsmGameObject gameObject;[Tooltip("GameObject to spawn at (optional).")]public FsmGameObject spawnPoint;[Tooltip("Position. If a Spawn Point is defined, this is used as a local offset from the Spawn Point position.")]public FsmVector3 position;[Tooltip("Minimum amount of objects to be spawned.")]public FsmInt spawnMin;[Tooltip("Maximum amount of objects to be spawned.")]public FsmInt spawnMax;[Tooltip("Minimum speed objects are fired at.")]public FsmFloat speedMin;[Tooltip("Maximum speed objects are fired at.")]public FsmFloat speedMax;[Tooltip("Minimum angle objects are fired at.")]public FsmFloat angleMin;[Tooltip("Maximum angle objects are fired at.")]public FsmFloat angleMax;[Tooltip("Randomises spawn points of objects within this range. Leave as 0 and all objects will spawn at same point.")]public FsmFloat originVariationX;public FsmFloat originVariationY;[Tooltip("Optional: Name of FSM on object you want to send an event to after spawn")]public FsmString FSM;[Tooltip("Optional: Event you want to send to object after spawn")]public FsmString FSMEvent;private float vectorX;private float vectorY;private bool originAdjusted;public override void Reset(){gameObject = null;spawnPoint = null;position = new FsmVector3{UseVariable = true};spawnMin = null;spawnMax = null;speedMin = null;speedMax = null;angleMin = null;angleMax = null;originVariationX = null;originVariationY = null;FSM = new FsmString{UseVariable = true};FSMEvent = new FsmString{UseVariable = true};}public override void OnEnter(){if (gameObject.Value != null){Vector3 a = Vector3.zero;Vector3 zero = Vector3.zero;if (spawnPoint.Value != null){a = spawnPoint.Value.transform.position;if (!position.IsNone){a += position.Value;}}else if (!position.IsNone){a = position.Value;}int num = Random.Range(spawnMin.Value, spawnMax.Value + 1);for (int i = 1; i <= num; i++){//TODO:以后创造完对象池后记得替换掉GameObject gameObject = GameObject.Instantiate(this.gameObject.Value, a, Quaternion.Euler(zero));float x = gameObject.transform.position.x;float y = gameObject.transform.position.y;float z = gameObject.transform.position.z;if (originVariationX != null){x = gameObject.transform.position.x + Random.Range(-originVariationX.Value, originVariationX.Value);originAdjusted = true;}if (originVariationY != null){y = gameObject.transform.position.y + Random.Range(-originVariationY.Value, originVariationY.Value);originAdjusted = true;}if (originAdjusted){gameObject.transform.position = new Vector3(x, y, z);}base.CacheRigidBody2d(gameObject);float num2 = Random.Range(speedMin.Value, speedMax.Value);float num3 = Random.Range(angleMin.Value, angleMax.Value);vectorX = num2 * Mathf.Cos(num3 * 0.017453292f);vectorY = num2 * Mathf.Sin(num3 * 0.017453292f);Vector2 velocity;velocity.x = vectorX;velocity.y = vectorY;rb2d.velocity = velocity;if (!FSM.IsNone){FSMUtility.LocateFSM(gameObject, FSM.Value).SendEvent(FSMEvent.Value);}}}Finish();}}}

二、第二个代表性场景

1.制作敌人沃姆Worm

说起来你看到这名字可能想不起来这是啥怪物,其实就是遗忘十字路中恶心玩家的那个路障

首先是灰尘粒子系统:

这个石头也是粒子系统:

这个也是粒子系统石头,区别在于,上面那个是Idle状态下播放的粒子系统,而这个是在Burst状态下播放的:

 

这个虫子属于是既能伤害玩家又能伤害敌人的,因此添加上DamageHero和DamageEnemy两个脚本: 

这里我们把将骨钉攻击那期的Nail Slash里面的playmakerfsm“damages_enemy”也给它安排上:

还有自己的Worm Control:

 向上中:

缩回去: 

 已经缩回地里:

是否开启burst rocks的particlesystem:

Burst的时候就在这里开启伤害和Collider: 

自定义行为脚本如下:

using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory("Particle System")][Tooltip("Set particle emission on or off on an object with a particle emitter")]public class SetParticleEmission : FsmStateAction{[RequiredField][Tooltip("The particle emitting GameObject")]public FsmOwnerDefault gameObject;public FsmBool emission;public override void Reset(){gameObject = null;emission = false;}public override void OnEnter(){if (gameObject != null){GameObject ownerDefaultTarget = Fsm.GetOwnerDefaultTarget(gameObject);if (ownerDefaultTarget != null){ownerDefaultTarget.GetComponent<ParticleSystem>().enableEmission = emission.Value;}}Finish();}}
}
using HutongGames.PlayMaker;
using UnityEngine;[ActionCategory("Hollow Knight")]
public class SetDamageHeroAmount : FsmStateAction
{[UIHint(UIHint.Variable)]public FsmOwnerDefault target;public FsmInt damageDealt;public override void Reset(){target = new FsmOwnerDefault();damageDealt = null;}public override void OnEnter(){GameObject safe = target.GetSafe(this);if(safe != null){DamageHero component = safe.GetComponent<DamageHero>();if(component != null && !damageDealt.IsNone){component.damageDealt = damageDealt.Value;}}base.Finish();}}

至此我们制作了一个流动的循环。 

2.制作可交互对象

其实主要就是这些杆子,我们用脚本breakable.cs来制作

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Breakable : MonoBehaviour,IHitResponder
{private Collider2D bodyCollider;[Tooltip("Renderer which presents the undestroyed object.")][SerializeField] private Renderer wholeRenderer;[Tooltip("List of child game objects which also represent the whole object.")][SerializeField] public  GameObject[] wholeParts;[Tooltip("List of child game objects which represent remnants that remain static after destruction.")][SerializeField] private GameObject[] remnantParts;[SerializeField] private List<GameObject> debrisParts;[SerializeField] private float angleOffset = -60f;[Tooltip("Breakables behind this threshold are inert.")][SerializeField] private float inertBackgroundThreshold;[Tooltip("Breakables in front of this threshold are inert.")][SerializeField] private float inertForegroundThreshold;[Tooltip("Breakable effects are spawned at this offset.")][SerializeField] private Vector3 effectOffset;[Tooltip("Prefab to spawn for audio.")][SerializeField] private AudioSource audioSourcePrefab;[Tooltip("Table of audio clips to play upon break.")][SerializeField] private AudioEvent breakAudioEvent;[Tooltip("Table of audio clips to play upon break.")][SerializeField] private RandomAudioClipTable breakAudioClipTable;[Tooltip("Prefab to spawn when hit from a non-down angle.")][SerializeField] private Transform dustHitRegularPrefab;[Tooltip("Prefab to spawn when hit from a down angle.")][SerializeField] private Transform dustHitDownPrefab;[Tooltip("Prefab to spawn when hit from a down angle.")][SerializeField] private float flingSpeedMin;[Tooltip("Prefab to spawn when hit from a down angle.")][SerializeField] private float flingSpeedMax;[Tooltip("Strike effect prefab to spawn.")][SerializeField] private Transform strikeEffectPrefab;[Tooltip("Nail hit prefab to spawn.")][SerializeField] private Transform nailHitEffectPrefab;[Tooltip("Spell hit effect prefab to spawn.")][SerializeField] private Transform spellHitEffectPrefab;[Tooltip("Object to send HIT event to.")][SerializeField] private GameObject hitEventReciever;[Tooltip("Forward break effect to sibling FSMs.")][SerializeField] private bool forwardBreakEvent;[Space]public Probability.ProbabilityGameObject[] containingParticles;public FlingObject[] flingObjectRegister;private bool isBroken;private void Awake(){bodyCollider = GetComponent<Collider2D>();}protected void Reset(){inertBackgroundThreshold = 1f;inertForegroundThreshold = -1f;effectOffset = new Vector3(0f, 0.5f, 0f);flingSpeedMin = 10f;flingSpeedMax = 17f;}protected void Start(){CreateAdditionalDebrisParts(debrisParts);float z = transform.position.z;if(z > inertBackgroundThreshold || z < inertForegroundThreshold){BoxCollider2D component = GetComponent<BoxCollider2D>();if(component != null){component.enabled = false;}Destroy(this);return;}for (int i = 0; i < remnantParts.Length; i++){GameObject gameObject = remnantParts[i];if(gameObject != null && gameObject.activeSelf){gameObject.SetActive(false);}}angleOffset *= Mathf.Sign(transform.localScale.x);}protected virtual void CreateAdditionalDebrisParts(List<GameObject> debrisParts){}public void Hit(HitInstance damageInstance){if (isBroken){return;}Debug.LogFormat("Breakable Take Hit");float impactAngle = damageInstance.Direction;float num = damageInstance.MagnitudeMultiplier;if(damageInstance.AttackType == AttackTypes.Spell){Instantiate(spellHitEffectPrefab, base.transform.position, Quaternion.identity).SetPositionZ(0.0031f);}else{if (damageInstance.AttackType != AttackTypes.Nail && damageInstance.AttackType != AttackTypes.Generic){impactAngle = 90f;num = 1f;}Instantiate(strikeEffectPrefab, base.transform.position,Quaternion.identity);Vector3 position = (damageInstance.Source.transform.position + base.transform.position) * 0.5f;SpawnNailHitEffect(nailHitEffectPrefab, position, impactAngle);}int cardinalDirection = DirectionUtils.GetCardinalDirection(damageInstance.Direction);Transform transform = dustHitRegularPrefab;float flingAngleMin;float flingAngleMax;Vector3 euler;if (cardinalDirection == 2){angleOffset *= -1f;flingAngleMin = 120f;flingAngleMax = 160f;euler = new Vector3(180f, 90f, 270f);}else if (cardinalDirection == 0){flingAngleMin = 30f;flingAngleMax = 70f;euler = new Vector3(0f, 90f, 270f);}else if (cardinalDirection == 1){angleOffset = 0f;flingAngleMin = 70f;flingAngleMax = 110f;num *= 1.5f;euler = new Vector3(270f, 90f, 270f);}else{angleOffset = 0f;flingAngleMin = 160f;flingAngleMax = 380f;transform = dustHitDownPrefab;euler = new Vector3(-72.5f, -180f, -180f);}if(transform != null){Instantiate(transform, transform.position + effectOffset, Quaternion.Euler(euler));}Break(flingAngleMin, flingAngleMax, num);}private static Transform SpawnNailHitEffect(Transform nailHitEffectPrefab, Vector3 position, float impactAngle){if (nailHitEffectPrefab == null)return null;int cardinalDirection = DirectionUtils.GetCardinalDirection(impactAngle);float y = 1.5f;float minInclusive;float maxInclusive;if (cardinalDirection == 3){minInclusive = 270f;maxInclusive = 290f;}else if (cardinalDirection == 1){minInclusive = 70f;maxInclusive = 110f;}else{minInclusive = 340f;maxInclusive = 380f;}float x = (cardinalDirection == 2) ? -1.5f : 1.5f;Transform transform = Instantiate(nailHitEffectPrefab,position,Quaternion.identity);Vector3 eulerAngles = transform.eulerAngles;eulerAngles.z = Random.Range(minInclusive, maxInclusive);transform.eulerAngles = eulerAngles;Vector3 localScale = transform.localScale;localScale.x = x;localScale.y = y;transform.localScale = localScale;return transform;}public void Break(float flingAngleMin, float flingAngleMax, float impactMultiplier){if (isBroken)return;SetStaticPartsActivation(true);for (int i = 0; i < debrisParts.Count; i++){GameObject gameObject = debrisParts[i];if (gameObject == null){Debug.LogErrorFormat(this, "Unassigned debris part in {0}", new object[]{this});}else{gameObject.SetActive(true);gameObject.transform.SetRotationZ(gameObject.transform.localEulerAngles.z + angleOffset);Rigidbody2D component = gameObject.GetComponent<Rigidbody2D>();if (component != null){float num = Random.Range(flingAngleMin, flingAngleMax);Vector2 a = new Vector2(Mathf.Cos(num * 0.017453292f), Mathf.Sin(num * 0.017453292f));float d = Random.Range(flingSpeedMin, flingSpeedMax) * impactMultiplier;component.velocity = a * d;}}}if (containingParticles.Length != 0){GameObject gameObject2 = Probability.GetRandomGameObjectByProbability(containingParticles);if (gameObject2){if (gameObject2.transform.parent != transform){FlingObject flingObject = null;foreach (FlingObject flingObject2 in flingObjectRegister){if (flingObject2.referenceObject == gameObject2){flingObject = flingObject2;break;}}if (flingObject != null){flingObject.Fling(transform.position);}else{gameObject2 = Instantiate(gameObject2, transform.position, Quaternion.identity);}}gameObject2.SetActive(true);}}breakAudioEvent.SpawnAndPlayOneShot(audioSourcePrefab, transform.position);breakAudioClipTable.SpawnAndPlayOneShot(audioSourcePrefab, transform.position);if (hitEventReciever != null){FSMUtility.SendEventToGameObject(hitEventReciever, "HIT", false);}if (forwardBreakEvent){FSMUtility.SendEventToGameObject(gameObject, "BREAK", false);}GameObject gameObject3 = GameObject.FindGameObjectWithTag("CameraParent");if (gameObject3 != null){PlayMakerFSM playMakerFSM = PlayMakerFSM.FindFsmOnGameObject(gameObject3, "CameraShake");if(playMakerFSM != null){playMakerFSM.SendEvent("EnemyKillShake");}}wholeRenderer.enabled = false;bodyCollider.enabled = false;isBroken = true;}private void SetStaticPartsActivation(bool v){}[System.Serializable]public class FlingObject{public GameObject referenceObject;[Space]public int spawnMin;public int spawnMax;public float speedMin;public float speedMax;public float angleMin;public float angleMax;public Vector2 originVariation;public FlingObject(){spawnMin = 25;spawnMax = 35;speedMin = 9f;speedMax = 20f;angleMin = 20f;angleMax = 160f;originVariation = new Vector2(0.5f, 0.5f);}public void Fling(Vector3 origin){if (!referenceObject){return;}int num = Random.Range(spawnMin, spawnMax + 1);for (int i = 0; i < num; i++){//TODO:Object PoolGameObject gameObject = Instantiate(referenceObject);if (gameObject){gameObject.transform.position = origin + new Vector3(Random.Range(-originVariation.x, originVariation.x), Random.Range(-originVariation.y, originVariation.y), 0f);float num2 = Random.Range(speedMin, speedMax);float num3 = Random.Range(angleMin, angleMax);float x = num2 * Mathf.Cos(num3 * 0.017453292f);float y = num2 * Mathf.Sin(num3 * 0.017453292f);Vector2 force = new Vector2(x, y);Rigidbody2D component = gameObject.GetComponent<Rigidbody2D>();if (component){component.AddForce(force, ForceMode2D.Impulse);}}}}}
}

 其三个子对象,一个是杆的顶部top,一个是杆的底部base,最后一个是破坏后蹦出来的石头particlesystem:

制作好一个后,我们就可以照此制作更多的这种可破坏的杆子Pole了,

还有这种可破坏的雕塑,原理都是一样的,只不过这个没有头部只有底部,而且有两个粒子系统:

 

 三、第三个代表性场景

            这个夭折了因为CSDN提示我上传的图片到达上限了,这我是没想到的,没办法这一集只能分为上下两期来讲了,而且下期我将讲述一个非常劲爆的敌人,感兴趣的话就等我一两个小时把内容整理完成发出来吧。

        md,我没想到CSDN有规矩24小时内只能上传300张图片,喜提一天冷却时间,大伙只能等我明天再来把这篇和下一篇文章写完了。

总结

        终于过了一天的坐牢期可以发图片出来了,我就把这一期的效果图展示在这里吧。

再来看看阿斯匹德的效果

再来看看孵化虫Hatcher

然后再来看看沃姆worm的效果:

相关文章:

[Unity Demo]从零开始制作空洞骑士Hollow Knight第十六集(上篇):制作更多地图,更多敌人,更多可交互对象

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、第一个代表性场景 1.制作敌人僵尸跳跳虫更多敌人2.制作敌人阿斯匹德更多可交互对象3.制作敌人孵化虫和它的孩子二、第二个代表性场景 1.制作更多敌人2.制作…...

Soap-UI传参

<?xml version"1.0" encoding"UTF-8"?> <soapenv:Envelope xmlns:soapenv"http://schemas.xmlsoap.org/soap/envelope/" xmlns:web"http://webservice.ihosp.jxmns.thirdparty.his.gocent.com/"><soapenv:Header/>…...

Centos7搭建minio对象存储服务器

Centos7搭建minio对象存储服务器 安装二进制程序配置服务文件 安装二进制程序 参考&#xff1a;https://segmentfault.com/q/1010000042181876 minio中国版&#xff1a;https://www.minio.org.cn/download.shtml#/linux # 下载二进制程序 wget https://dl.min.io/server/min…...

递归算法之汉诺塔问题(Tower of Hanoi)详细解读

汉诺塔问题&#xff08;Tower of Hanoi&#xff09;是一个经典的递归问题&#xff0c;由法国数学家 douard Lucas 于1883年提出。问题描述了如何将不同大小的圆盘从一个柱子移到另一个柱子&#xff0c;同时遵循特定规则。它是计算机科学中用来展示递归思想和算法设计的经典案例…...

软件设计模式------工厂方法模式

工厂方法模式&#xff08;Factory Method Pattern&#xff09;&#xff0c;又称工厂模式&#xff0c;也叫虚拟构造器模式&#xff08;Virtual Constructor Pattern&#xff09;或多态工厂模式&#xff08;Polymorphic Pactory Pattern&#xff09;,属于类创建型模式。 我们知道…...

演示:基于WPF的DrawingVisual开发的高刷新率示波器

一、目的&#xff1a;分享一个基于WPF的DrawingVisual开发的高刷新率示波器 二、效果演示 特此说明&#xff1a;由于Gif录制工具帧率不够&#xff0c;渲染60帧用了4.6秒&#xff0c;平均帧率在12Hz左右&#xff0c;所以展示效果不好&#xff0c;想要看好些的效果可以看文章下面…...

git入门操作(2)

文章目录 git入门操作&#xff08;2&#xff09;git diff 查看差异git diff gitignore忽略文件1.在代码仓库创建这个文件2.添加对 log 文件过滤 连接远程仓库与ssh配置远程仓库和本地仓库关联步骤分支基本操作步骤命令&#xff1a; 合并冲突分支合并逻辑1.新建分支 dev&#xf…...

【AI学习】扩散模型学习总结PPT

#1024程序员节&#xff5c;征文# 看了一些文章&#xff0c;大概学习了扩散模型。 《李宏毅 2023 最新 Diffusion Model 原理讲解》&#xff08;文章链接&#xff1a;https://zhuanlan.zhihu.com/p/692430885&#xff09; 《What are Diffusion Models?》 https://lilianwen…...

【Python】相等性比较运算(==, is)的学习笔记

1. 相等性比较运算&#xff1a; & is Python中有两种比较运算符和is&#xff1b; 和 is 的主要区别在于它们比较的对象属性不同&#xff1a; 运算符&#xff1a; 比较对象的值或内容是否相等。调用对象的 __eq__() 方法来进行比较。可以被重载&#xff08;在自定义类中重…...

智慧公厕厂家:智慧公厕建设推动城市公厕智能化变革

随着科技的不断进步&#xff0c;智慧公厕厂家正以创新之力推动着城市公厕的智能化变革。 一、提升用户体验 智慧公厕为使用者带来了前所未有的便利。通过实时显示厕位使用情况&#xff0c;避免了旅客的无效排队&#xff0c;节省了时间。感应式设备如水龙头、洗手液等&#xff…...

大一计算机课程之线性代数

《大一计算机课程之线性代数》 在大一的计算机课程中&#xff0c;线性代数是一门极为重要的基础学科&#xff0c;它就像一把神奇的钥匙&#xff0c;为计算机科学领域的诸多方面开启了智慧之门。 线性代数主要研究线性方程组、向量空间、线性变换等内容。对于计算机专业的学生…...

什么是运动控制器?运动控制器的特点

运动控制器是一种专门用于控制机械系统中运动部件&#xff08;如电机、驱动器和其他运动元件&#xff09;的电子设备。它们在自动化、机器人、数控机床、工业自动化和精密控制系统等领域得到了广泛应用。 运动控制器的功能 运动控制器主要负责以下几个方面的功能&#xff1a;…...

[AWS]RDS数据库版本升级

背景&#xff1a;由于AWS上mysql5.7版本不再支持&#xff0c;需要进行版本升级。 吐槽&#xff1a;每年都要来那么几次&#xff0c;真的有病一样&#xff0c;很烦。 步骤一、升级检查 AWS提供了一个python的升级检测脚本&#xff0c;可以按照一下脚本下载测试&#xff1a; [r…...

(Golang)初识Go语言!!为什么选择Go?如何配置Go的开发环境?VS Code如何配置Go环境?

1. Go能做什么&#xff1f; go的优点&#xff1a;运行速度快、并发能力强 Go的应用领域&#xff1a; 区块链应用&#xff08;BT、分布式账本技术&#xff09; 后端服务应用 例如&#xff1a; 美团后台流量支撑程序 支撑主站的后台流量&#xff08;排序、推荐、搜索等&#xf…...

【人工智能-初级】第15章 TensorFlow 和 PyTorch 的入门:深度学习的利器

文章目录 一、引言二、TensorFlow 简介2.1 什么是 TensorFlow&#xff1f;2.2 TensorFlow 安装2.3 TensorFlow 构建简单的神经网络2.4 TensorBoard 可视化 三、PyTorch 简介3.1 什么是 PyTorch&#xff1f;3.2 PyTorch 安装3.3 PyTorch 构建简单的神经网络 四、TensorFlow 与 P…...

git禁用 SSL 证书验证

命令 git config --global http.sslVerify false注意&#xff1a;禁用 SSL 证书验证是不安全的&#xff0c;可能会使你的 Git 操作面临中间人攻击的风险。因此&#xff0c;只有在你确信网络环境是安全的&#xff0c;且了解禁用 SSL 验证的后果时&#xff0c;才应该使用这个配置…...

C++之《剑指offer》学习记录(2):sizeof

笔者最近在找工作时&#xff0c;无意间读到了一本名为《剑指offer》的书&#xff0c;粗略翻阅了一下&#xff0c;感觉这将会是一本能让我不再苦恼于笔试和面试“手搓代码”的书。故笔者写下该系列博客记录自己的学习历程&#xff0c;希望能和这本书的读者朋友们一起交流学习心得…...

linux线程 | 同步与互斥 | 线程池以及知识点补充

前言&#xff1a;本节内容是linux的线程的相关知识。本篇首先会实现一个简易的线程池&#xff0c; 然后再将线程池利用单例的懒汉模式改编一下。 然后再谈一些小的知识点&#xff0c;比如自旋锁&#xff0c; 读者写者问题等等。 那么&#xff0c; 现在开始我们的学习吧。 ps:本…...

ArkTS 如何实现表单,地区选择效果

速览 ArkTS实现表单和地区选择效果,可通过Picker组件实现地区选择下拉列表,结合表单组件如Input等构建完整表单。使用ArkTS提供的UI组件库和状态管理机制,可以方便地构建复杂且交云互动的表单界面。 1. ArkTS 表单基础 在ArkTS中,构建表单通常涉及多个UI组件的组合,如I…...

Vite 项目的核心配置- vite.config.ts 和 tsconfig.json 全解析

一、vite.config.ts 详细说明 vite.config.ts 是 Vite 项目的核心配置文件。它允许你自定义 Vite 的行为,以适应你的项目需求。 让我们来看看其中一些重要的配置选项: import { fileURLToPath, URL } from node:url// 使用 defineConfig 帮手函数&#xff0c;这样不用 jsdoc …...

如何使用JMeter进行性能测试的保姆级教程

性能测试是确保网站在用户访问高峰时保持稳定和快速响应的关键环节。作为初学者&#xff0c;选择合适的工具尤为重要。JMeter 是一个强大的开源性能测试工具&#xff0c;可以帮助我们轻松模拟多用户场景&#xff0c;测试网站的稳定性与性能。本教程将引导你通过一个简单的登录场…...

Qt 实战(11)样式表 | 11.1、样式表简介

文章目录 一、样式表简介1、简介2、样式表语法2.1、样式规则2.2、选择器类型2.3、伪状态2.4、设置子控件状态 3、样式表继承与优先级3.1、样式表继承3.2、样式表优先级3.3、解决冲突3.4、样式表层叠 4、总结 前言&#xff1a; 在开发图形用户界面&#xff08;GUI&#xff09;应…...

WebGl 多缓冲区和数据偏移

1.多缓冲区 多缓冲区技术通常涉及到创建多个缓冲区对象&#xff0c;并将它们用于不同的数据集。这种做法可以提高数据处理效率&#xff0c;尤其是在处理大量数据或需要频繁更新数据时。通过预先分配和配置多个缓冲区&#xff0c;可以在不影响渲染性能的情况下&#xff0c;快速…...

基于SSM的甜品店销售管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…...

Spacetime Gaussian Feature Splatting for Real-Time Dynamic View Synthesis

Spacetime Gaussian Feature Splatting for Real-Time Dynamic View Synthesis 摘要 动态场景的新视角合成一直是一个引人入胜但充满挑战的问题。尽管最近取得了很多进展&#xff0c;但如何同时实现高分辨率的真实感渲染、实时渲染和紧凑的存储&#xff0c;依然是一个巨大的…...

PCL 基于FPFH特征描述子获取点云对应关系

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 FPFH特征计算函数 2.1.2 获取点云之间的对应点对函数 2.1.3 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接: PCL点云算法与项目实战案例汇总…...

项目实战:Qt+OpenCV仿射变换工具v1.1.0(支持打开图片、输出棋盘角点、调整偏移点、导出变换后的图等等)

若该文为原创文章&#xff0c;转载请注明出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/143105881 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、Op…...

OpenCV坐标系统与图像处理案例

在图像处理中&#xff0c;理解图像的坐标系统是至关重要的。OpenCV&#xff0c;作为一个强大的计算机视觉库&#xff0c;提供了丰富的功能来操作图像。本文将介绍OpenCV中的坐标系统&#xff0c;并提供一个简单的案例来展示如何使用这些坐标来修改图像的特定区域。 OpenCV坐标…...

Unity之如何使用Unity Cloud Build云构建

文章目录 前言什么是 UnityCloudBuild?如何使用Unity云构建Unity 团队中的人员不属于 Unity Team 的人员UnityCloudBuild2.0价格表如何使用Unity云构建配置CloudBuild前言 Unity Cloud Build作为Unity平台的一项强大工具,它允许开发团队通过云端自动构建项目,节省了繁琐的手…...

Halcon开启多线程

并行运算&#xff08;提升检测时间&#xff09; 支持主线程中的子线程并行执行程序和调用算子。 一旦启动&#xff0c;子线程由线程 ID 标识&#xff0c;该线程 ID 是一个取决于操作系统的整数进程号。 子线程的执行独立于它们启动的线程。 因此&#xff0c;无法预测子线程执行…...