[Unity Demo]从零开始制作空洞骑士Hollow Knight第五集:再制作更多的敌人

server/2024/11/15 1:01:09/

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

文章目录

  • 前言
  • 一、制作敌人另个爬虫Crawler
    • 1.公式化导入制作另个爬虫Crawler素材
    • 2.制作另个爬虫Crawler的Crawler.cs状态机
    • 3.制作敌人另个爬虫Crawler的playmaker状态机
  • 二、制作敌人飞虫Fly
    • 1.公式化导入制作飞虫Fly素材
    • 2.制作敌人飞虫Fly的playmaker状态机
  • 总结


前言

如标题所示,最近感觉有些没活了,或者说是趁热打铁再制作更多的敌人,于是本期的主角就决定是两个大家刚进入空洞骑士就经常看到的敌人另一个爬虫Crawler和飞虫Fly。


一、制作敌人另一个爬虫Crawler

1.公式化导入制作另个爬虫Crawler素材

首先我们先完成Crawler的完整行为,第一步导入素材,分别为它制作tk2dspritecollection和tk2dspriteanimation, 

 

  然后就到了公式化设置一个敌人的时候了Rb2d , audiosource, boxcollider2d:

可以看到它不需要其它的子物体,因为只需要执行行走,死亡,转向的状态。 

2.制作另一个爬虫Crawler的Crawler.cs状态机

创建同名函数给该游戏对象

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Crawler : MonoBehaviour
{public float speed;[Space]private Transform wallCheck; //墙面检测的位置private Transform groundCheck; //地面检测的位置private Vector2 velocity; //记录速度private CrawlerType type;private Rigidbody2D body;private tk2dSpriteAnimator anim;private void Awake(){body = GetComponent<Rigidbody2D>();anim = GetComponent<tk2dSpriteAnimator>();}private void Start(){float z = transform.eulerAngles.z;//通过transform.eulerAngles.z来判断哪种类型的Crawlerif (z >= 45f && z <= 135f){type = CrawlerType.Wall;velocity = new Vector2(0f, Mathf.Sign(-transform.localScale.x) * speed);}else if (z >= 135f && z <= 225f){type = ((transform.localScale.y > 0f) ? CrawlerType.Roof : CrawlerType.Floor);velocity = new Vector2(Mathf.Sign(transform.localScale.x) * speed, 0f);}else if (z >= 225f && z <= 315f){type = CrawlerType.Wall;velocity = new Vector2(0f, Mathf.Sign(transform.localScale.x) * speed);}else{type = ((transform.localScale.y > 0f) ? CrawlerType.Floor : CrawlerType.Roof);velocity = new Vector2(Mathf.Sign(-transform.localScale.x) * speed, 0f);}//TODO:CrawlerType crawlerType = type;if(crawlerType != CrawlerType.Floor){if(crawlerType - CrawlerType.Roof <= 1){body.gravityScale = 0;//如果在墙面面上rb2d的重力就设置为1}}else{body.gravityScale = 1; //如果在地面上rb2d的重力就设置为1//TODO:}StartCoroutine(Walk());}/// <summary>/// 使用协程实现Walk函数,循环直至hit=true后挂起然后启用协程Turn()/// </summary>/// <returns></returns>private IEnumerator Walk(){for(; ; ){anim.Play("Walk");body.velocity = velocity;bool hit = false;while (!hit){if(CheckRayLocal(wallCheck.localPosition,transform.localScale.x > 0f ? Vector2.left : Vector2.right, 1f)){hit = true;break;}if (!CheckRayLocal(groundCheck.localPosition, transform.localScale.y > 0f ? Vector2.down : Vector2.up, 1f)){hit = true;break;}yield return null;}yield return StartCoroutine(Turn());yield return null;}}/// <summary>/// 使用协程实现转向函数/// </summary>/// <returns></returns>private IEnumerator Turn(){body.velocity = Vector2.zero;yield return StartCoroutine(anim.PlayAnimWait("Turn"));transform.SetScaleX(transform.localScale.x * -1f);velocity.x = velocity.x * -1f;velocity.y = velocity.y * -1f;}/// <summary>/// 发射射线,检测是否有LayerMask.GetMask("Terrain").collider/// </summary>/// <param name="originLocal"></param>/// <param name="directionLocal"></param>/// <param name="length"></param>/// <returns></returns>public bool CheckRayLocal(Vector3 originLocal, Vector2 directionLocal, float length){Vector2 vector = transform.TransformPoint(originLocal);Vector2 vector2 = transform.TransformDirection(directionLocal);RaycastHit2D raycastHit2D = Physics2D.Raycast(vector, vector2, length, LayerMask.GetMask("Terrain"));Debug.DrawLine(vector, vector + vector2 * length);return raycastHit2D.collider != null;}private enum CrawlerType{Floor,Roof,Wall}
}

回到Unity编辑器中,如果你的是开始往左走的话,记得设置speed为负数

只需要这个就行了。我们不需要挂载Lineofsightdetector.cs,这个就是敌人发现敌人到自己攻击范围的脚本

3.制作敌人另个爬虫Crawler的playmaker状态机

创建一个名字叫Crawler的playmaker状态机:

变量和事件如下所示:

同样我们还需要自定义脚本:

using System.Collections;
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory("Enemy AI")]public class WalkLeftRight : FsmStateAction{private Rigidbody2D body;private tk2dSpriteAnimator spriteAnimator;private Collider2D collider;public FsmOwnerDefault gameObject;public float walkSpeed; //移动速度public bool spriteFacesLeft; //sprite开始时是向左的吗public string groundLayer; //也就是Terrainpublic float turnDelay; //转向延时private float nextTurnTime; //下一次转身的时间[Header("Animation")]public FsmString walkAnimName; //walk的动画名字public FsmString turnAnimName; //turn的动画名字public FsmBool startLeft;public FsmBool startRight;public FsmBool keepDirection;private float scaleX_pos;private float scaleX_neg;private const float wallRayHeight = 0.5f; //检测墙壁的射线高度private const float wallRayLength = 0.1f; //检测墙壁的射线长度private const float groundRayLength = 1f; //检测地面的射线高度private GameObject target; //目标private Coroutine walkRoutine; //walk的协程private Coroutine turnRoutine; //turn的协程private bool shouldTurn; //应该转身了吗private float Direction{get{if (target){return Mathf.Sign(target.transform.localScale.x) * (spriteFacesLeft ? -1 : 1); //记录方向属性}return 0f;}}public override void OnEnter(){UpdateIfTargetChanged();SetupStartingDirection();walkRoutine = StartCoroutine(Walk());}/// <summary>/// 退出时停掉所有正在执行的协程/// </summary>public override void OnExit(){if(walkRoutine != null){StopCoroutine(walkRoutine);walkRoutine = null;}if (turnRoutine != null){StopCoroutine(turnRoutine);turnRoutine = null;}}/// <summary>/// 如果目标target发生变化后重新初始化/// </summary>private void UpdateIfTargetChanged(){GameObject ownerDefaultTarget = Fsm.GetOwnerDefaultTarget(gameObject);if(ownerDefaultTarget != target){target = ownerDefaultTarget;body = target.GetComponent<Rigidbody2D>();collider = target.GetComponent<Collider2D>();spriteAnimator = target.GetComponent<tk2dSpriteAnimator>();}}private IEnumerator Walk(){if (spriteAnimator){spriteAnimator.Play(walkAnimName.Value);}for(; ; ){if (body){Vector2 velocity = body.velocity;velocity.x = walkSpeed * Direction;body.velocity = velocity;if(shouldTurn || (CheckIsGrounded() && (CheckWall() || CheckFloor()) && Time.time >= nextTurnTime)){shouldTurn = false;nextTurnTime = Time.time + turnDelay;turnRoutine = StartCoroutine(Turn());yield return turnRoutine;}}yield return new WaitForFixedUpdate();}}private IEnumerator Turn(){Vector2 velocity = body.velocity;velocity.x = 0f;body.velocity = velocity;tk2dSpriteAnimationClip clipByName = spriteAnimator.GetClipByName(turnAnimName.Name);if(clipByName != null){float seconds = clipByName.frames.Length / clipByName.fps;//计算出动画播放的时间spriteAnimator.Play(clipByName);yield return new WaitForSeconds(seconds);}Vector3 localScale = target.transform.localScale;localScale.x *= -1f;target.transform.localScale = localScale;if (spriteAnimator){spriteAnimator.Play(walkAnimName.Value);}turnRoutine = null;}/// <summary>/// 检测是否接触到墙面/// </summary>/// <returns></returns>private bool CheckWall(){Vector2 vector = collider.bounds.center + new Vector3(0f, -(collider.bounds.size.y / 2f) + wallRayHeight);Vector2 vector2 = Vector2.right * Direction;float num = collider.bounds.center.x / 2f + wallRayLength;Debug.DrawLine(vector, vector + vector2 * num);return Physics2D.Raycast(vector, vector2, num, LayerMask.GetMask(groundLayer)).collider != null;}/// <summary>/// 检测是否接触到地板/// </summary>/// <returns></returns>private bool CheckFloor(){Vector2 vector = collider.bounds.center + new Vector3((collider.bounds.size.x / 2f + wallRayLength) * Direction, -(collider.bounds.size.y / 2f) + wallRayHeight);Debug.DrawLine(vector, vector + Vector2.down * groundRayLength);return !(Physics2D.Raycast(vector, Vector2.down, groundRayLength, LayerMask.GetMask(groundLayer)).collider != null);}/// <summary>/// 检测是否已经接触到地面/// </summary>/// <returns></returns>private bool CheckIsGrounded(){Vector2 vector = collider.bounds.center + new Vector3(0f,-(collider.bounds.center.y / 2f) + wallRayHeight);Debug.DrawLine(vector, vector + Vector2.down * groundRayLength);return Physics2D.Raycast(vector, Vector2.down, groundRayLength, LayerMask.GetMask(groundLayer)).collider != null;}/// <summary>/// 设置开始时GameObject的方向/// </summary>private void SetupStartingDirection(){if (target.transform.localScale.x < 0f){if (!spriteFacesLeft && startRight.Value){shouldTurn = true;}if (spriteFacesLeft && startLeft.Value){shouldTurn = true;}}else{if (spriteFacesLeft && startRight.Value){shouldTurn = true;}if (!spriteFacesLeft && startLeft.Value){shouldTurn = true;}}if (!startLeft.Value && !startRight.Value && !keepDirection.Value && UnityEngine.Random.Range(0f, 100f) <= 50f)//随机选择一边{shouldTurn = true;}startLeft.Value = false;startRight.Value = false;}public WalkLeftRight(){walkSpeed = 4f;groundLayer = "Terrain";turnDelay = 1f;}}}

整个PLAYmaker状态机如下所示:

 

 

 

二、制作敌人飞虫Fly

1.公式化导入制作飞虫Fly素材

这里就不过多赘述了,直接上图

 

 

这里需要注意到Fly有两个子游戏对象,用于检测是否碰到其它敌人需要转向,只不过这些都是后面设计要用的,所以先不管:

 

 还有就是我们要开始导入PlayMaker Unity 2D用于检测场景中涉及到playmaker 2d的物理碰撞:

这里是我已经导入了所以是灰色的,你导入后会发现多生成了一个预制体:

 再给我们fly添加一个脚本叫PlayMakerUnity2DProxy.cs:

2.制作敌人飞虫Fly的playmaker状态机

完成上述过程中就到了创建playmaker状态机环节,在这里我只用一个playmaker状态机完成Fly完整的循环:

变量和事件如下所示:

此时还需要继续自定义脚本,这就要用到上面提到的 playmaker 2d的物理碰撞了:

using UnityEngine;
using System.Collections.Generic;
using System;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Physics)][Tooltip("Detect additional collisions between the Owner of this FSM and other object with additional raycasting.")]public class CheckCollisionSideEnter : FsmStateAction{[UIHint(UIHint.Variable)]public FsmBool topHit;[UIHint(UIHint.Variable)]public FsmBool rightHit;[UIHint(UIHint.Variable)]public FsmBool bottomHit;[UIHint(UIHint.Variable)]public FsmBool leftHit;public FsmEvent topHitEvent;public FsmEvent rightHitEvent;public FsmEvent bottomHitEvent;public FsmEvent leftHitEvent;public bool otherLayer;public int otherLayerNumber;public FsmBool ignoreTriggers;private PlayMakerUnity2DProxy _proxy;private Collider2D col2d;private const float RAYCAST_LENGTH = 0.08f;private List<Vector2> topRays;private List<Vector2> rightRays;private List<Vector2> bottomRays;private List<Vector2> leftRays;public override void Reset(){}public override void OnEnter(){col2d = Fsm.GameObject.GetComponent<Collider2D>();_proxy = Owner.GetComponent<PlayMakerUnity2DProxy>();if(_proxy == null){_proxy = Owner.AddComponent<PlayMakerUnity2DProxy>();}_proxy.AddOnCollisionEnter2dDelegate(new PlayMakerUnity2DProxy.OnCollisionEnter2dDelegate(DoCollisionEnter2D));}public override void OnUpdate(){    		}public override void OnExit(){_proxy.RemoveOnCollisionEnter2dDelegate(new PlayMakerUnity2DProxy.OnCollisionEnter2dDelegate(DoCollisionEnter2D));}public new void DoCollisionEnter2D(Collision2D collision){if (!otherLayer){if(LayerMask.LayerToName(collision.gameObject.layer) == "Terrain"){CheckTouching(LayerMask.NameToLayer("Terrain"));return;}}else{CheckTouching(otherLayerNumber);}}private void CheckTouching(LayerMask layer){topRays = new List<Vector2>();topRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.max.y));topRays.Add(new Vector2(col2d.bounds.center.x, col2d.bounds.max.y));topRays.Add(col2d.bounds.max);rightRays = new List<Vector2>();rightRays.Add(col2d.bounds.max);rightRays.Add(new Vector2(col2d.bounds.max.x, col2d.bounds.center.y));rightRays.Add(new Vector2(col2d.bounds.max.x, col2d.bounds.min.y));bottomRays = new List<Vector2>();bottomRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.min.y));bottomRays.Add(new Vector2(col2d.bounds.center.x, col2d.bounds.min.y));bottomRays.Add(col2d.bounds.min);leftRays = new List<Vector2>();leftRays.Add(col2d.bounds.min);leftRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.center.y));leftRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.max.y));topHit.Value = false;rightHit.Value = false;bottomHit.Value = false;leftHit.Value = false;foreach (Vector2 v in topRays){RaycastHit2D raycastHit2D = Physics2D.Raycast(v, Vector2.up, RAYCAST_LENGTH, 1 << layer);if(raycastHit2D.collider != null && (!ignoreTriggers.Value || !raycastHit2D.collider.isTrigger)){topHit.Value = true;Fsm.Event(topHitEvent);break;}}foreach (Vector2 v2 in rightRays){RaycastHit2D raycastHit2D2 = Physics2D.Raycast(v2, Vector2.right, RAYCAST_LENGTH, 1 << layer);if (raycastHit2D2.collider != null && (!ignoreTriggers.Value || !raycastHit2D2.collider.isTrigger)){rightHit.Value = true;Fsm.Event(rightHitEvent);break;}}foreach (Vector2 v3 in bottomRays){RaycastHit2D raycastHit2D3 = Physics2D.Raycast(v3, Vector2.down, RAYCAST_LENGTH, 1 << layer);if(raycastHit2D3.collider != null && (!ignoreTriggers.Value || !raycastHit2D3.collider.isTrigger)){bottomHit.Value = true;Fsm.Event(bottomHitEvent);break;}}foreach (Vector2 v4 in leftRays){RaycastHit2D raycastHit2D4 = Physics2D.Raycast(v4, Vector2.left, RAYCAST_LENGTH, 1 << layer);if (raycastHit2D4.collider != null && (!ignoreTriggers.Value || !raycastHit2D4.collider.isTrigger)){leftHit.Value = true;Fsm.Event(leftHitEvent);break;}}}}}

 

using UnityEngine;
using System.Collections.Generic;
using System;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Physics)][Tooltip("Detect additional collisions between the Owner of this FSM and other object with additional raycasting.")]public class CheckCollisionSide : FsmStateAction{[UIHint(UIHint.Variable)]public FsmBool topHit;[UIHint(UIHint.Variable)]public FsmBool rightHit;[UIHint(UIHint.Variable)]public FsmBool bottomHit;[UIHint(UIHint.Variable)]public FsmBool leftHit;public FsmEvent topHitEvent;public FsmEvent rightHitEvent;public FsmEvent bottomHitEvent;public FsmEvent leftHitEvent;public bool otherLayer;public int otherLayerNumber;public FsmBool ignoreTriggers;private PlayMakerUnity2DProxy _proxy;private Collider2D col2d;private const float RAYCAST_LENGTH = 0.08f;private List<Vector2> topRays;private List<Vector2> rightRays;private List<Vector2> bottomRays;private List<Vector2> leftRays;private bool checkUp;private bool checkRight;private bool checkBottom;private bool checkLeft;public override void Reset(){checkUp = false;checkRight = false;checkBottom = false;checkLeft = false;}public override void OnEnter(){col2d = Fsm.GameObject.GetComponent<Collider2D>();topRays = new List<Vector2>(3);rightRays = new List<Vector2>(3);bottomRays = new List<Vector2>(3);leftRays = new List<Vector2>(3);_proxy = Owner.GetComponent<PlayMakerUnity2DProxy>();if (_proxy == null){_proxy = Owner.AddComponent<PlayMakerUnity2DProxy>();}_proxy.AddOnCollisionStay2dDelegate(new PlayMakerUnity2DProxy.OnCollisionStay2dDelegate(DoCollisionStay2D));if(!topHit.IsNone || topHitEvent != null){checkUp = true;}else{checkUp = false;}if (!rightHit.IsNone || rightHitEvent != null){checkRight = true;}else{checkRight = false;}if (!bottomHit.IsNone || bottomHitEvent != null){checkBottom = true;}else{checkBottom = false;}if (!leftHit.IsNone || leftHitEvent != null){checkLeft = true;}else{checkLeft = false;}}public override void OnUpdate(){if(topHit.Value || rightHit.Value || bottomHit.Value || leftHit.Value){if (!otherLayer){CheckTouching(LayerMask.NameToLayer("Terrain"));return;}CheckTouching(otherLayerNumber);}	}public override void OnExit(){_proxy.RemoveOnCollisionStay2dDelegate(new PlayMakerUnity2DProxy.OnCollisionStay2dDelegate(DoCollisionStay2D));}public new void DoCollisionStay2D(Collision2D collision){if (!otherLayer){if(collision.gameObject.layer == LayerMask.NameToLayer("Terrain")){CheckTouching(LayerMask.NameToLayer("Terrain"));return;}}else{CheckTouching(otherLayerNumber);}}public new void DoCollisionExit2D(Collision2D collision){topHit.Value = false;rightHit.Value = false;bottomHit.Value = false;leftHit.Value = false;}private void CheckTouching(LayerMask layer){if (checkUp){topRays.Clear();topRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.max.y));topRays.Add(new Vector2(col2d.bounds.center.x, col2d.bounds.max.y));topRays.Add(col2d.bounds.max);topHit.Value = false;for (int i = 0; i < 3; i++){RaycastHit2D raycastHit2D = Physics2D.Raycast(topRays[i], Vector2.up, RAYCAST_LENGTH, 1 << layer);if(raycastHit2D.collider != null && (!ignoreTriggers.Value || !raycastHit2D.collider.isTrigger)){topHit.Value = true;Fsm.Event(topHitEvent);break;}}}if (checkRight){rightRays.Clear();rightRays.Add(col2d.bounds.max);rightRays.Add(new Vector2(col2d.bounds.max.x, col2d.bounds.center.y));rightRays.Add(new Vector2(col2d.bounds.max.x, col2d.bounds.min.y));rightHit.Value = false;for (int i = 0; i < 3; i++){RaycastHit2D raycastHit2D2 = Physics2D.Raycast(rightRays[i], Vector2.right, RAYCAST_LENGTH, 1 << layer);if (raycastHit2D2.collider != null && (!ignoreTriggers.Value || !raycastHit2D2.collider.isTrigger)){rightHit.Value = true;Fsm.Event(rightHitEvent);break;}}}if (checkBottom){bottomRays.Clear();bottomRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.min.y));bottomRays.Add(new Vector2(col2d.bounds.center.x, col2d.bounds.min.y));bottomRays.Add(col2d.bounds.min);for (int i = 0; i < 3; i++){RaycastHit2D raycastHit2D3 = Physics2D.Raycast(bottomRays[i], Vector2.down, RAYCAST_LENGTH, 1 << layer);if (raycastHit2D3.collider != null && (!ignoreTriggers.Value || !raycastHit2D3.collider.isTrigger)){bottomHit.Value = true;Fsm.Event(bottomHitEvent);break;}}}if (checkLeft){leftRays.Clear();leftRays.Add(col2d.bounds.min);leftRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.center.y));leftRays.Add(new Vector2(col2d.bounds.min.x, col2d.bounds.max.y));for (int i = 0; i < 3; i++){RaycastHit2D raycastHit2D4 = Physics2D.Raycast(leftRays[i], Vector2.left, RAYCAST_LENGTH, 1 << layer);if (raycastHit2D4.collider != null && (!ignoreTriggers.Value || !raycastHit2D4.collider.isTrigger)){leftHit.Value = true;Fsm.Event(leftHitEvent);return;}}}}public enum CollisionSide { top,left,right,bottom,other}}}
using System;
using UnityEngine;namespace HutongGames.PlayMaker.Actions
{[ActionCategory(ActionCategory.Physics2D)][Tooltip("Sets the 2d Velocity of a Game Object, using an angle and a speed value. For the angle, 0 is to the right and the degrees increase clockwise.")]public class SetVelocityAsAngle : RigidBody2dActionBase{[RequiredField][CheckForComponent(typeof(Rigidbody2D))]public FsmOwnerDefault gameObject;[RequiredField]public FsmFloat angle;[RequiredField]public FsmFloat speed;private FsmFloat x;private FsmFloat y;public bool everyFrame;public override void Reset(){gameObject = null;angle = new FsmFloat{UseVariable = true};speed = new FsmFloat{UseVariable = true};everyFrame = false;}public override void Awake(){Fsm.HandleFixedUpdate = true;}public override void OnPreprocess(){Fsm.HandleFixedUpdate = true;}public override void OnEnter(){CacheRigidBody2d(Fsm.GetOwnerDefaultTarget(gameObject));DoSetVelocity();if (!everyFrame){Finish();}}public override void OnFixedUpdate(){DoSetVelocity();if (!everyFrame){Finish();}}private void DoSetVelocity(){if (rb2d == null)return;x = speed.Value * Mathf.Cos(angle.Value * 0.017453292f); //将角度转化为速度y = speed.Value * Mathf.Sin(angle.Value * 0.017453292f);Vector2 velocity;velocity.x = x.Value;velocity.y = y.Value;rb2d.velocity = velocity;}}}

 整个Playmaker状态机如下所示:

 

 

完整图如下所示:


总结

首先我们来看看Crawler的转向效果能不能实现:

 

我们再来看看Fly的状态机能不能正常运行:

我们可以创建一个闭环的四边形,并给他一个"Terrain"Layer

 

完美运行,下一期我们来丰富一下玩家的行为吧。


http://www.ppmy.cn/server/119334.html

相关文章

测试ASP.NET Core的WebApi项目调用WebService

虚拟机中部署的匿名访问的WebService&#xff0c;支持简单的加减乘除操作。本文记录在WebApi中调用该WebService的方式。   VS2022创建WebApi项目&#xff0c;然后在解决方案资源管理器的Connected Services节点点右键&#xff0c;选择管理连接的服务菜单。 点击下图圈红处…

平安养老险阜阳中心支公司开展金融教育宣传专项活动

为全面深入开展“金融教育宣传月”的各项工作&#xff0c;不断完善金融惠民利民举措&#xff0c;提升金融服务质效&#xff0c;帮助基层群众增强维权意识、防非反诈的自我保护能力。近日&#xff0c;平安养老保险股份有限公司&#xff08;以下“平安养老险”&#xff09;阜阳中…

iOS 巨魔神器,Geranium 天竺葵:6大功能,个个都解决痛点

嘿&#xff0c;这是黑猫。如果你装了巨魔&#xff0c;却只知道安装第三方APP&#xff0c;那就是暴殄天物。巨魔的价值不仅是应用侧载&#xff0c;还有强大的玩机工具生态——这也是我花费大量时间&#xff0c;去制作巨魔精选IPA合集的原因。 通过巨魔商店安装的APP&#xff0c…

JavaScript如何判断输入的是空格

1、JavaScript如何判断输入的是空格 1.1. 使用trim()方法和length属性 trim() 方法可以移除字符串两端的空白字符&#xff08;包括空格、制表符、换行符等&#xff09;&#xff0c;然后检查处理后的字符串长度是否为0。 function isOnlySpaces(str) {return str.trim().lengt…

Vue2+vue-office/excel 实现在线加载Excel文件预览

附件预览无数据&#xff1f; 竟然是样式问题&#xff0c;必须加上style"height: 100vh;"。。。。。。。。。。。。。

JDBC与MyBatis:数据库访问技术的变迁【后端 15】

JDBC与MyBatis&#xff1a;数据库访问技术的变迁 JDBC的基本使用 Java Database Connectivity (JDBC) 是Java提供的一种标准API&#xff0c;用于与数据库进行交互。它提供了一系列的接口和类&#xff0c;使得开发人员能够直接使用Java代码来编写SQL语句并执行数据库操作。JDBC…

选址模型 | 基于混沌模拟退火粒子群优化算法的电动汽车充电站选址与定容(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于混沌模拟退火粒子群优化算法的电动汽车充电站选址与定容&#xff08;Matlab&#xff09; 问题建模&#xff1a;首先&#xff0c;需要将电动汽车充电站选址与定容问题进行数学建模&#xff0c;确定目标函数和约束…

电子电气架构 --- 基于ISO 26262的车载电子软件开发流程

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…