*胡闹厨房*
前期准备
详细教程
一、创建项目
1、选择Universal 3D,创建项目
2、删除预制文件Readme:点击Remove Readme Assets,弹出框上点击Proceed
3、Edit-Project Setting-Quality,只保留High Fidelity
4、打开 Assets-Settings ,保留URP-HighFidelity-Renderer 和 URP-HighFidelity 文件
5、新建Scripts、Prefabs、ScripatableObjects、Scripts/ScripatableObjects文件夹
二、导入资源
1、将资源拖入Project,在弹出框中点击Inport
2、重命名场景为GameScene
三、增加输入系统
1、安装Input System,注意:点击No
2、设置输入系统
(1) Edit-Project Setting,选择Player-Other Settings,找到Configuration
(2) Active Input Handling 可选框,选择Both,弹出框选择Apply,Unity编辑器自动重启
3、添加输入事件:Settings文件夹下Create-Input Actions,命名为PlayerInputActions,双击打开
4、设置用键盘字母WASD的输入事件
(1) 添加输入事件:点击左上角 “+” 号命名为Player,将New action重命名为Move
(2) 更改Action Type和Control Type的值分别为 Value 和 Vector2
(3) 删除Move下的No Bingding,点击Move右侧 “+” 号,选择Add Up\Down\left\Right Composite
(4) 命名事件为WASD,点击Up…,点击Path可选框,在蓝色区域输入对应字母,选择Keyboard
(5) 点击 Save Asset
5、用键盘上的箭头控制的输入事件
(1) 点击Move右侧 “+” 号,选择Add Up\Down\left\Right Composite
(2) 命名事件为Arrow Keys,点击Up…,Path可选框,点击listen,输入对应箭头,选择Keyboard
(3) 点击 Save Asset
6、添加游戏手柄控制
(1) 电脑连接手柄,按动摇杆
(2) 选中Move,点击+号,弹出窗口选择Add Binding
(3) 点击Path右侧可选框,Joystick-listen,摇动摇杆,回车(或在Path中输入Left Stick)
(4) 点击 Save Asset
7、调整手柄控制
(1) 选中 Left Stick,点击Processors右侧加号,选择Stick Deadzone
(2) 取消勾选Min 右侧的Default,输入0.5
四、关联角色和输入事件
1、选中 Setting文件夹中的 PlayerInputActions
2、自动生成脚本:勾选Generate C# Class,点击Apply
后期效果处理
一、添加地板
1、3D Object-Plane,重命名为Floor,Reset它的Transform,Scale为5,5,5
2、设置材质:在Inspector面板下找到Mesh Renderer,设置Materials下的Element 0为Floor
二、虚拟相机
1、安装Cinemachine包
2、创建虚拟相机:GameObject-Cinemachine-Virtual Camera
3、设置虚拟相机
(1) Position:0.75,21.5,-20.79;Rotation为46,0,0
(2) Lens Vertical FOV:20
三、视觉效果
1、选中Settings文件夹下的URP-HighFidelity
(1) 设置Quality下的HDR为勾选状态,
(2) Anti Aliasing(MSAA)改为8x(抗锯齿)
2、设置URP-HighFidelity-Renderer下的Screen Space Ambient Occlusion(屏幕空间环境光遮蔽)
Intensity=4,Radius=0.3,DirectLighting Strength =1
3、创建新的Volume配置文件
(1) 方法:选中Global Volume,在Inspector面板找到Volume下的Profile,点击New
(2) 目的:用于定义和存储Volume的全局设置,包括各种后处理效果和其他渲染相关的参数。
(3) 作用:通过创建新的Profile,用户可以自定义和调整这些设置,以满足不同场景和渲染需求。
4、设置 Global Volume
(1) 点击Add Override,添加Tonemapping(颜色校正),勾选 Mode,选择Neutrual
(2) 点击Add Override,添加Color Adjustments:勾选Contrast,设为20;勾选Saturation,设为20
(3) 点击Add Override,添加Bloom(泛光):勾选Threshold,设为0.95;勾选Intensity,设为1
(4) 点击Add Override,添加Vignette(晕影):勾选Intensity,设为0.25;勾选Smoothness,0.4
四、背景音乐
1、Create Empy,命名为MusicManager,Reset Transform
2、为MusicManager添加Audio Source组件
(1) 设置Audio Source下的AudioClip为Music
(2) 勾选Play On Awake,勾选Loop(循环)
(3) Priority为0(播放优先级最高),Volume为0.5
3、确保Main Camera上有Audio Listener组件
五、音效
1、Create Empy,命名为SoundManager,Reset Transform
2、音效对象
(1) 在Scripts/ScripatableObjects文件夹下新建AudioClipRefsSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class AudioClipRefsSO : ScriptableObject
{public AudioClip[] chop;public AudioClip[] deliveryFail;public AudioClip[] deliverySuccess;public AudioClip[] footstep;public AudioClip[] objectDrop;public AudioClip[] objectPickup;public AudioClip[] trash;public AudioClip[] warning;public AudioClip stoveSizzle;
}
(2) 在Assets文件夹新建ScriptableObjects文件夹
(3) ScriptableObjects文件夹下制作 AudioClipRefsSO对象,命名为AudioClipRefsSO,赋值
3、音效管理:为SoundManager添加SoundManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SoundManager : MonoBehaviour
{// 定义一个常量字符串,用于在PlayerPrefs中存储音效音量的键private const string PLAYER_PREFS_SOUND_EFFECTS_VOLUME = "SoundEffectsVolume";public static SoundManager Instance { get; private set; }[SerializeField] private AudioClipRefsSO audioClipRefsSO;private float volume = 1f;private void Awake(){Instance = this;// 从PlayerPrefs中读取音效音量,如果不存在则默认为1fvolume = PlayerPrefs.GetFloat(PLAYER_PREFS_SOUND_EFFECTS_VOLUME, 1f);}private void PlaySound(AudioClip[] audioClipArray, Vector3 position, float volume = 1f){if (audioClipArray != null && audioClipArray.Length > 0){PlaySound(audioClipArray[Random.Range(0, audioClipArray.Length)], position, volume);}else{Debug.LogWarning("Audio clip array is null or empty.");}}private void PlaySound(AudioClip audioClip, Vector3 position, float volumeMultiplier = 1f){AudioSource.PlayClipAtPoint(audioClip, position, volumeMultiplier * volume);}public void ChangeVolume(float amount = 0.1f){volume += amount;if (volume > 1f){volume = 0f;}PlayerPrefs.SetFloat(PLAYER_PREFS_SOUND_EFFECTS_VOLUME, volume);// PlayerPrefs.Save();}public float GetVolume(){return volume;}
}
六、背景
1、围墙
(1) 3D-Cube Object,命名为Wall,Scale.x = 0.25,y=3,z=12.68,Position.x=8.4,y=1.37,z=-2.02
(2) 将Mesh Renderer下的Materials下的Element0 设置为_Assets/Materials中的Wall
(3) 复制Wall,Position.x=-6.9
(4) 复制Wall,Scale.z=15.53,Rotation.y= 90,Position.x=0.78,z=4.41
2、墙外
(1) 3D-Cube Object,Scale.x= 7.69,y=3.17,z=12.95,Position.x,y,z为12.36,1.27,-1.95,材质Black
(2) 复制Cube,Position.x=-10.87
(3) 复制Cube,Rotation.y= 90,Scale.z=31.5,Position.x=1.266,z=8.33
3、组织层级:选中三面墙和三块Cube,Create Empty Parent,命名为Walls
场景
一、定义场景
定义场景、设置加载场景的方法:新建Loader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;public class Loader
{public enum Scene { MainMenuScene, GameScene, LoadingScene, }private static Scene targetScene;public static void Load(Scene targetScene){Loader.targetScene = targetScene;// 加载LoadingScene场景SceneManager.LoadScene(Scene.LoadingScene.ToString());}// 回调方法,用于从LoadingScene场景加载目标场景public static void LoaderCallback(){SceneManager.LoadScene(targetScene.ToString());}
}
二、开始场景
1、场景:在Scenes文件夹新建一个场景,命名为MainMenuScene
2、画布:打开MainMenuScene,UI-Canvas
(1) Canvas Scaler下的UI Scale Mode改为Scale With Screen Size
(2) Reference Resolution为1920 1080,Match为1,(完全匹配高度)
3、视觉效果:打开GameScene,复制Global Volume和Floor,粘贴到MainMenuScene中
4、设置镜头
(1) GameObject-CineMachine-Virtual Camera
(2) Pos.x=-0.51,y= 0.44,z=-1.74 Rotation.x = -16.638,y=18.495
(3) Noise选择Basic Multi Channel Perlin,Noise Profile选择Handheld_normal_mild
(4) Frequency Gain为0.5,Amplitude Gain为1
5、按钮:以Canvas为父物体,Create Empty,命名为MainMenuUI,Alt+拉伸
(1) 开始按钮
① 以MainMenuUI为父物体,UI-Button,命名为PlayButton,width = 450,Height= 150
② 选中PlayButton,锚定左下,Pos.x = 324 Pos.y = 336。按钮颜色3A3A3A
③ 给按钮添加Outline组件,Effect Color 中透明度255。Effect Distance下, x= 3 y= 3
④ 给按钮添加Shadow组件,Effect Distance下, x=5 y=-5
⑤ 展开PlayButton,文本内容为PLAY,加粗,字号70,白色
(2) 退出按钮
① 复制PlayButton,重命名为QuitButton。Pos.y = 127。Height= 120
② 展开QuitButton,文本内容为QUIT。字号58.7
(3) 设置按钮:给MainMenuUI添加MainMenuUI.cs组件
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class MainMenuUI : MonoBehaviour
{[SerializeField] private Button playButton;[SerializeField] private Button quitButton;private void Awake(){playButton.onClick.AddListener(() =>{Loader.Load(Loader.Scene.GameScene);});quitButton.onClick.AddListener(() =>{Application.Quit();});// 确保暂停之后重新游戏时游戏时间流速正常Time.timeScale = 1.0f;}
}
(4) 赋值
5、美化
(1) 导入角色
① 将_Assets/PrefabsVisuals中的PlayerViusal拖入场景:Pos.x=1.17,z=0.84,Rotation.y=-134
② 复制得到3个PlayerViusal,Pos.x=3.53,z=1.07,Rotation.y=-129
Pos.x=3.33,z=2.17,,Rotation.y=-136.6
Pos.x=0.71,z=2.27,Rotation.y=-153
③ 展开PlayerVisual (3),选中Head和Body,设置Materials下的Element 0为PlayerBody_Red,
④ 同样的方法设置PlayerVisual (3)的子物体head和body为蓝色,PlayerVisual (1)为绿色
(2) 添加图片
① 以 MainMenuUI 为父物体,UI-Image,Source Image为KitchenChaosLogo
② 锚点为左上,Width=881.72,Height=493.318,Pos.X=492,Y=-307
三、加载场景
1、场景:在Scenes文件夹新建一个场景,命名为LoadingScene
2、相机背景
(1) 选中Main Camera,Inspetor面板展开Camera-Environment
(2) Background Type为Solid Color,Background为黑色
3、画布:UI-Canvas
(1) Canvas Scaler下的UI Scale Mode改为Scale With Screen Size
(2) Reference Resolution为1920 1080,Match为1,(完全匹配高度)
4、文本:
(1) UI-Text,Pos.X= -883,Pos.Y= -418。宽高 都为0
(2) 文本内容:LOADING... 文字加粗、字号55,不换行,白色
5、切换场景
(1) 目的:LoadingScene场景加载完毕后调用Loader类中的回调方法(进入游戏场景GameScene)
(2) Create Empty,命名为LoaderCallback
(3) 为LoaderCallback添加LoaderCallback.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LoaderCallback : MonoBehaviour
{private bool isFirstUpdate = true;private void Update(){if (isFirstUpdate){isFirstUpdate = false;Loader.LoaderCallback();}}
}
角色控制器
一、创建角色
1、打开GameScene,Create Empty,命名为Player,重置Transform
2、将PlayerVisual作为Player的子物体拖放到Hierarchy面板上,重置Transform
3、以Player为父物体,Create Empty,命名为KitchenObjectHoldPoint,position.y = 1.456,z=1
二、角色运动
1、获取玩家的输入
(1) Create Empty,命名为GameInput,用于承载处理输入的逻辑。重置Transform
(2) 获取玩家输入并将其转换为一个归一化的 Vector2
向量:为GameInput 添加GameInput.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GameInput : MonoBehaviour
{private PlayerInputActions playerInputActions;private void Awake(){playerInputActions = new PlayerInputActions();//调用 PlayerInputActions 类中的 Player 动作组的 Enable 方法,使玩家输入生效playerInputActions.Player.Enable();}public Vector2 GetMovementVectorNormalized(){Vector2 inputVector = playerInputActions.Player.Move.ReadValue<Vector2>();inputVector = inputVector.normalized;Debug.Log(inputVector);return inputVector;}
}
2、角色移动和旋转 :
(1) 为Player添加Player.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : MonoBehaviour
{public static Player Instance { get; private set; }[SerializeField] private float moveSpeed = 7.0f;[SerializeField] private GameInput gameInput;private bool isWalking;private void Awake(){if (Instance != null){Debug.LogError("There is more than one Player instance");}Instance = this;}private void Update(){HandleMovement();}public bool IsWalking(){return isWalking;}private void HandleMovement(){Vector2 inputVector = gameInput.GetMovementVectorNormalized();Vector3 moveDir = new(inputVector.x, 0f, inputVector.y);transform.position += moveDir * moveSpeed * Time.deltaTime;// 判断moveDir是否不等于0向量,再将判断结果赋值给isWalkingisWalking = moveDir != Vector3.zero;float rotationSpeed = 10f;transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotationSpeed);}
}
(2) 赋值
3、根据玩家的行走状态实时更新动画,关联动画与行走状态
(1) 为PlayerVisual添加PlayerAnimator.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerAnimator : MonoBehaviour
{private const string IS_WALKING = "IsWalking";[SerializeField] private Player player;private Animator animator;private void Awake(){animator = GetComponent<Animator>();}private void Update(){animator.SetBool(IS_WALKING, player.IsWalking());}
}
(2) 赋值
3、角色移动粒子特效
(1) 将_Assets/PrefabsVisuals中的PlayerMovingParticles作为Player的子物体
(2) Rotation.x=-90
四、角色移动音效
1、脚步音效的方法:编辑SoundManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SoundManager : MonoBehaviour
{private const string PLAYER_PREFS_SOUND_EFFECTS_VOLUME = "SoundEffectsVolume";public static SoundManager Instance { get; private set; }[SerializeField] private AudioClipRefsSO audioClipRefsSO;private float volume = 1f;private void Awake(){Instance = this;volume = PlayerPrefs.GetFloat(PLAYER_PREFS_SOUND_EFFECTS_VOLUME, 1f);}// 脚步音效public void PlayFootstepSound(Vector3 position,float volume){PlaySound(audioClipRefsSO.footstep,position,volume);}private void PlaySound(AudioClip[] audioClipArray, Vector3 position, float volume = 1f){if (audioClipArray != null && audioClipArray.Length > 0){PlaySound(audioClipArray[Random.Range(0, audioClipArray.Length)], position, volume);}else{Debug.LogWarning("Audio clip array is null or empty.");}}private void PlaySound(AudioClip audioClip, Vector3 position, float volumeMultiplier = 1f){AudioSource.PlayClipAtPoint(audioClip, position, volumeMultiplier * volume);}public void ChangeVolume(float amount = 0.1f){volume += amount;if (volume > 1f){volume = 0f;}PlayerPrefs.SetFloat(PLAYER_PREFS_SOUND_EFFECTS_VOLUME, volume);// PlayerPrefs.Save();}public float GetVolume(){return volume;}
}
2、调用音效:给Player添加PlayerSounds.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerSounds : MonoBehaviour
{private Player player;private float footstepTimer;private float footstepTimerMax = .1f;private void Awake(){player = GetComponent<Player>();}private void Update(){footstepTimer -= Time.deltaTime;if(footstepTimer < 0f){footstepTimer = footstepTimerMax;if (player.IsWalking()){float volume = 1f;SoundManager.Instance.PlayFootstepSound(player.transform.position, volume);}}}
}
五、碰撞检测
1、遇到障碍物后改变方向:编辑Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : MonoBehaviour
{public static Player Instance { get; private set; }[SerializeField] private float moveSpeed = 7.0f;[SerializeField] private GameInput gameInput;private bool isWalking;private void Awake(){if (Instance != null){Debug.LogError("There is more than one Player instance");}Instance = this;}private void Update(){HandleMovement();}public bool IsWalking(){return isWalking;}private void HandleMovement(){Vector2 inputVector = gameInput.GetMovementVectorNormalized();Vector3 moveDir = new(inputVector.x, 0f, inputVector.y);float moveDistance = moveSpeed * Time.deltaTime;float playerRadius = 0.7f;float playerHeight = 2f;// 判断是否发生碰撞,若发生,canMove的值为false;否则为true(未碰撞时角色可移动)bool canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDir, moveDistance);// 碰撞检测和尝试沿不同方向移动// 如果 !canMove的值为true(即如果发生碰撞,则canMove为false,!canMove就为true)if (!canMove){// 若发生碰撞,尝试沿x轴方向移动Vector3 moveDirX = new Vector3(moveDir.x, 0f, 0f).normalized;// 检查沿X轴方向移动是否可行,并且没有碰撞// 确保只有当玩家确实想要在 x 轴方向上移动(即不是微小的、几乎可以忽略不计的移动)时,才进行碰撞检测。canMove = (moveDir.x < -.5f || moveDir.x > +0.5f) && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDirX, moveDistance);if (canMove){// 如果可以沿X轴移动,则更新移动方向moveDir = moveDirX;}else{// 如果不能沿X轴移动,尝试沿Z轴方向移动Vector3 moveDirZ = new Vector3(0f, 0f, moveDir.z).normalized;canMove = (moveDir.z < -.5f || moveDir.z > 0.5f) && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDirZ, moveDistance);if (canMove){moveDir = moveDirZ;}else{// 不能向任何方向移动}}}// 确定可以移动时更新角色的位置// 如果最终确定可以移动(无论是沿X轴还是Z轴)if (canMove){transform.position += moveDir * moveDistance;}isWalking = moveDir != Vector3.zero;float rotationSpeed = 10f;transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotationSpeed);}
}
六、角色交互
1、创建测试用柜台
(1) 在Prefabs文件夹下,新建文件夹,命名为Counters
(2) Create Empty ,命名为_BaseCounter,Transform.Position为0,0,2.71,Rotation.y=-180
(3) 添加并设置 Box Collider组件,设置Center.y=0.5,Size为1.5,1.5,1.5
(4) 为_BaseCounter添加 Counters 图层,选择No,this object only
(5) 为_BaseCounter添加子物体
① Create Empty,命名为 CounterTopPoint
② 设置CounterTopPoint 的Transform,Position为0,1.3,0
(6) 在Scripts/Counters文件夹下,新建BaseCounter.cs(空内容)
(7) 将_BaseCounter制成预制体
(8) 制作_BaseCounter预制体变体,命名为ClearCounter,添加ClearCounter.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ClearCounter : BaseCounter
{}
① 选择ClearCounter_Visual作为Counter的子物体
② 复制ClearCounter_Visual,重命名为Selected,设置它的Scale为1.01,1.01,1.01
③ 选中Selected的子物体 KitchenCounter,更改材质为CounterSelected(设置灰度)
④ 禁用(隐藏)KitchenCounter
2、选中效果
(1) 向场景添加ClearCounter,Position为7.5,0,3.5;Rotation.y=-180
(2) 声明、触发委托,增加交互方法:编辑Player.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : MonoBehaviour
{public static Player Instance { get; private set; }public event EventHandler<OnSelectedCounterChangedEventArgs> OnSelectedCounterChanged;public class OnSelectedCounterChangedEventArgs : EventArgs{public BaseCounter SelectedCounter { get; }public OnSelectedCounterChangedEventArgs(BaseCounter selectedCounter){SelectedCounter = selectedCounter;}}[SerializeField] private float moveSpeed = 7.0f;[SerializeField] private GameInput gameInput;[SerializeField] private LayerMask countersLayerMask;private bool isWalking;private Vector3 lastInteractDir;private BaseCounter selectedCounter;private void Awake(){if (Instance != null){Debug.LogError("There is more than one Player instance");}Instance = this;}private void Update(){HandleMovement();HandleInteractions();}public bool IsWalking(){return isWalking;}private void HandleInteractions(){Vector2 inputVector = gameInput.GetMovementVectorNormalized();Vector3 moveDir = new Vector3(inputVector.x, 0f, inputVector.y);if (moveDir != Vector3.zero){lastInteractDir = moveDir;}float interactDistance = 2f;if (Physics.Raycast(transform.position, lastInteractDir, out RaycastHit raycastHit, interactDistance, countersLayerMask)){if (raycastHit.transform.TryGetComponent(out BaseCounter baseCounter)){// Has ClearCounterif (baseCounter != selectedCounter){SetSelectedCounter(baseCounter);}}else{SetSelectedCounter(null);}}else{SetSelectedCounter(null);}}private void HandleMovement(){Vector2 inputVector = gameInput.GetMovementVectorNormalized();Vector3 moveDir = new(inputVector.x, 0f, inputVector.y);float moveDistance = moveSpeed * Time.deltaTime;float playerRadius = 0.7f;float playerHeight = 2f;bool canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDir, moveDistance);if (!canMove){Vector3 moveDirX = new Vector3(moveDir.x, 0f, 0f).normalized;canMove = (moveDir.x < -.5f || moveDir.x > +0.5f) && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDirX, moveDistance);if (canMove){moveDir = moveDirX;}else{Vector3 moveDirZ = new Vector3(0f, 0f, moveDir.z).normalized;canMove = (moveDir.z < -.5f || moveDir.z > 0.5f) && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight, playerRadius, moveDirZ, moveDistance);if (canMove){moveDir = moveDirZ;}else{// 不能向任何方向移动}}}if (canMove){transform.position += moveDir * moveDistance;}isWalking = moveDir != Vector3.zero;float rotationSpeed = 10f;transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotationSpeed);}private void SetSelectedCounter(BaseCounter selectedCounter){this.selectedCounter = selectedCounter;OnSelectedCounterChanged?.Invoke(this,new OnSelectedCounterChangedEventArgs(selectedCounter));}
}
(3) 赋值:图层
(4) 为Selected 添加 SelectedCounterVisual.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SelectedCounterVisual : MonoBehaviour
{[SerializeField] private BaseCounter baseCounter;[SerializeField] private GameObject[] visualGameObjectArray;private void Start(){Player.Instance.OnSelectedCounterChanged += Player_OnSelectedCounterChanged;}private void Player_OnSelectedCounterChanged(object sender, Player.OnSelectedCounterChangedEventArgs e){if (e.SelectedCounter == baseCounter){Show();}else{Hide();}}private void Show(){foreach (GameObject visualGameObject in visualGameObjectArray){visualGameObject.SetActive(true);}}private void Hide(){foreach (GameObject visualGameObject in visualGameObjectArray){visualGameObject.SetActive(false);}}
}
食材和盘子
一、食材预制体
1、新建Prefabs文件夹,在Prefabs文件夹下,新建文件夹命名为 KitchenObjects
2、制作番茄预制体
(1) Create Empty,命名为Tomato,设置Transform,Position为 -2.16,0,0.75
(2) 添加子物体Tomato_Visual
(3) 将Tomato制成预制体,放入KitchenObjects文件夹。
(4) 删除Hierarchy面板上的Tomato
3、制作CheeseBlock预制体
(1) 复制预制体Tomato,重命名(F2)为CheeseBlock
(2) 打开CheeseBlock预制体
(3) 添加子物体 CheeseBlock_Visual,删除Tomato_Visual
4、同样的方法制作Bread、Cabbage、MeatPattyUncooked、MeatPattyCooked 预制体
5、番茄切片
(1) 复制Tomato 预制体,重命名为TomatoSlices
(2) 编辑TomatoSlices预制体:添加TomatoSlices,删除Tomato
6、同样的方法制作 CheeseSlices、CabbageSlices、MeatPattyBurned、Plate预制体
二、食材和盘子
2.1 食材
1、新建ScriptableObjects文件夹,其下新建KitchenObjectSO文件夹
2、食材信息管理:
(1) 在Scripts文件夹下新建 ScriptableObjects文件夹
(2) 在Scripts/ ScriptableObjects文件夹下新建 KitchenObjectSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class KitchenObjectSO : ScriptableObject
{public Transform prefab;public Sprite sprite;public string objectName;
}
3、创建并设置各食材
(1) 在KitchenObjectSO文件夹下,新建 Kitchen Object SO 文件,命名为Tomato
(2) 新建 Kitchen Object SO 文件,命名为TomatoSlices
(3) 创建kitchenObjectSO对象:CheeseBlock、Bread、Cabbage、MeatPattyUncooked
(4) 创建:CheeseSlices、CabbageSlices、MeatPattyCooked、MeatPattyBurned、Plate
4、关联食材与食材属性:
(1) 为除 Plate 外的所有食材预制体添加KitchenObject.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class KitchenObject : MonoBehaviour
{[SerializeField] private KitchenObjectSO kitchenObjectSO;public KitchenObjectSO GetKitchenObjectSO(){return kitchenObjectSO;}
}
(2) 分别赋值
2.2 盘子
1、设置可装盘的食材列表:
(1) 为 Plate 预制体添加PlateKitchenObject.cs组件
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlateKitchenObject : KitchenObject
{public event EventHandler<OnIngredientAddedEventArgs> OnIngredientAdded;public class OnIngredientAddedEventArgs : EventArgs{public KitchenObjectSO kitchenObjectSO;}// 可装盘的食材列表 [SerializeField] private List<KitchenObjectSO> validKitchenObjectSOList;// 食材列表private List<KitchenObjectSO> kitchenObjectSOList;private void Awake(){kitchenObjectSOList = new List<KitchenObjectSO>();}// 尝试将食材添加到食材列表public bool TryAddIngredient(KitchenObjectSO kitchenObjectSO){// 如果待添加的食材不在可装盘的食材列表中if (!validKitchenObjectSOList.Contains(kitchenObjectSO)){// Not a valid ingredientreturn false;// 添加失败}// 如果食材列表中已经包含待添加的食材if (kitchenObjectSOList.Contains(kitchenObjectSO)){// Already has this typereturn false;}else{kitchenObjectSOList.Add(kitchenObjectSO);OnIngredientAdded?.Invoke(this, new OnIngredientAddedEventArgs{kitchenObjectSO = kitchenObjectSO});return true;}}// 获取当前盘子中实际摆放的食材的列表public List<KitchenObjectSO> GetKitchenObjectSOList(){return kitchenObjectSOList;}
}
(3) 赋值
2、盘中物品显示:编辑 Plate 预制体
(1) 将_Assets/PrefabsVisuals文件夹下的PlateCompleteVisual预制体作为子物体添加到Plate上
(2) 订阅委托:给PlateCompleteVisual添加PlateCompleteVisual.cs组件
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlateCompleteVisual : MonoBehaviour
{[Serializable]public struct KitchenObjectSO_GameObject{public KitchenObjectSO kitchenObjectSO; // 存储食材的数据public GameObject gameObject; // 该食材在场景中的实例}[SerializeField] private PlateKitchenObject plateKitchenObject;// 存储食材对象设定和食材实例化物品的对应关系。// 指定盘子可以承载的所有食材及其对应的GameObject[SerializeField] private List<KitchenObjectSO_GameObject> kitchenObjectSOGameObjectList;private void Start(){plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;foreach (KitchenObjectSO_GameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList){kitchenObjectSOGameObject.gameObject.SetActive(false);}}private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.OnIngredientAddedEventArgs e){foreach (KitchenObjectSO_GameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList){if (kitchenObjectSOGameObject.kitchenObjectSO == e.kitchenObjectSO){kitchenObjectSOGameObject.gameObject.SetActive(true);}}}
}
(3) 赋值(注意:使用PlateCompleteVisual的子物体)
3、显示盘中食材图标
(1) 在Package Manager中安装 2D Sprite
(2) 设置 图标 UI
① 打开Plate预制体,新建一个Canvas重命名为PlateIconsUI
② 更改Canvas的Render Mode为World Space,Pos.x,z,都为0,宽,高都为0.9,Pos.y = 1
③ Create Empty 作为PlateIconsUI的子物体,命名为IconTemplate,宽高都为0.3
④ 新建 IconTemplate的子物体UI - Image,命名为Background,拉伸
⑤ 设置Image的Source Image:注意点击右上角眼睛图标(显示隐藏)后,搜索circle
⑥ 复制Background,重命名为Icon,设置Image的Source Image为Bread
⑦ Hierarchy的层次如下图
⑧ PlateIconsUI添加Grid Layout Group组件,Cell Size0.3,0.3,Child Alignment为Middle Center
(3) 某一个食材的图标
① 给IconTemplate添加PlateIconsSingleUI.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class PlateIconsSingleUI : MonoBehaviour
{[SerializeField] private Image image;public void SetKitchenObjectSO(KitchenObjectSO kitchenObjectSO){image.sprite = kitchenObjectSO.sprite;}
}
② 赋值
(4) 显示图标
① 给PlateIconsUI添加PlateIconsUI.cs组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlateIconsUI : MonoBehaviour
{[SerializeField] private PlateKitchenObject plateKitchenObject;[SerializeField] private Transform iconTemplate;private void Awake(){iconTemplate.gameObject.SetActive(false);}private void Start(){// 当有新成分被添加到盘子时,调用 PlateKitchenObject_OnIngredienAdded 方法plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;}private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.OnIngredientAddedEventArgs e){UpdateVisual();}private void UpdateVisual(){foreach (Transform child in transform){// 如果子物体是iconTemplate,跳过当前循环迭代,继续下一个循环迭代if (child == iconTemplate) continue;Destroy(child.gameObject);}foreach (KitchenObjectSO kitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList()){Transform iconTransform = Instantiate(iconTemplate, transform);iconTransform.gameObject.SetActive(true);iconTransform.GetComponent<PlateIconsSingleUI>().SetKitchenObjectSO(kitchenObjectSO);}}
}
② 赋值
(5) 改变图标朝向:子物体 PlateIconsUI 添加LookAtCamera.cs组件,Mode改为Camera Forward
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class LookAtCamera : MonoBehaviour
{private enum Mode { LootAt, LookAtInverted, CameraForward, CameraForwardInverted, }[SerializeField] private Mode mode;private void LateUpdate(){switch (mode){case Mode.LootAt:transform.LookAt(Camera.main.transform);break;case Mode.LookAtInverted:Vector3 dirFromCamera = transform.position - Camera.main.transform.position;transform.LookAt(transform.position + dirFromCamera);break;case Mode.CameraForward:transform.forward = Camera.main.transform.forward;break;case Mode.CameraForwardInverted:transform.forward = -Camera.main.transform.forward;break;}}
}
5、关联食材与食材切片
(1) 在Scripts/ ScriptableObjects文件夹下新建 CuttingRecipeSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class CuttingRecipeSO : ScriptableObject
{public KitchenObjectSO input;public KitchenObjectSO output;public int cuttingProgressMax;
}
(2) 在ScriptableObjects下新建 CuttingRecipeSO文件夹
(3) 在CuttingRecipeSO文件夹下创建CuttingRecipeSO对象,命名为Tomato-TomatoSilices赋值
(4) 分别创建和设置:Cabbage-CabbageSlices(max=5)、CheeseBlock-CheeseSlices(3)
9、关联生、熟肉饼
(1) 在Scripts/ ScriptableObjects文件夹下新建FryingRecipeSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class FryingRecipeSO : ScriptableObject
{public KitchenObjectSO input;public KitchenObjectSO output;public float fryingTimerMax;
}
(2) 在ScriptableObjets文件夹下新建FryingRecipeSO文件夹
(3) 新建FryingRecipeSO对象,命名为MeatPattyUncooked-MeatPattyCooked,Max=5
10、关联熟、焦肉饼
(1) 在Scripts/ScriptableObjects文件夹,新建BurningRecipeSO.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class BurningRecipeSO : ScriptableObject
{public KitchenObjectSO input;public KitchenObjectSO output;public float burningTimerMax;
}
(2) 在ScriptableObjects下新建BurningRecipeSO文件夹
(3) 创建BurningRecipeSO对象:新建MeatPattyCooked-MeatPattyBurned,Max=6
橱柜
一、橱柜总属性
1、在Scripts文件夹下新建 Interface 文件夹
2、创建接口,用于定义食材的父对象(厨柜、玩家)的行为:新建 IkitchenObjectParent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface IkitchenObjectParent
{//获取食材的Transformpublic Transform GetKitchenObjectFollowTransform();//设置食材public void SetKitchenObject(KitchenObject kitchenObject);//拿取食材public KitchenObject GetKitchenObject();//清除食材public void ClearKitchenObject();//检查是否有食材public bool HasKitchenObject();
}
3、实现接口
(1) 编辑BaseCounter.cs ,用于处理橱柜的基本行为和交互
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BaseCounter : MonoBehaviour, IKitchenObjectParent
{[SerializeField] private Transform counterTopPoint;private KitchenObject kitchenObject;public virtual void Interact(Player player){Debug.LogError("BaseCounter.Interact();");}public virtual void InteractAlternate(Player player){//Debug.LogError("BaseCounter.InteractAlternate();");}public Transform GetKitchenObjectFollowTransform(){return counterTopPoint;}public void SetKitchenObject(KitchenObject kitchenObject){this.kitchenObject = kitchenObject;}public KitchenObject GetKitchenObject(){retu
相关文章:
*胡闹厨房*
前期准备 详细教程 一、创建项目 1、选择Universal 3D,创建项目 2、删除预制文件Readme:点击Remove Readme Assets,弹出框上点击Proceed 3、Edit-Project Setting-Quality,只保留High Fidelity 4、打开 Assets-Settings ,保留URP-HighFidelity-Renderer 和 URP-High…...
.NET 8 项目 Docker 方式部署到 Linux 系统详细操作步骤
本文将详细介绍如何将一个 .NET 8 项目通过 Docker 部署到 Linux 系统中。以下步骤包括从项目的创建、Dockerfile 的编写、镜像构建、到最后在 Linux 上的容器运行。 1. 环境准备 在开始之前,请确保你已经具备以下环境: Linux 系统(如 Ubu…...
状态码对照表
别瞎自定义状态码了 1xx:信息性状态码 状态码名称使用场景100Continue客户端应继续请求,等待后续响应。101Switching Protocols服务器根据客户端的请求切换协议。102Processing服务器正在处理请求,但尚未完成。103Early Hints提供给客户端的…...
【愚公系列】《循序渐进Vue.js 3.x前端开发实践》027-组件的高级配置和嵌套
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...
Autosar-Os是怎么运行的?(多核系统运行)
写在前面: 入行一段时间了,基于个人理解整理一些东西,如有错误,欢迎各位大佬评论区指正!!! 目录 1.Autosar多核操作系统 1.1多核启动过程 1.2多核运行过程 1.2.1核间任务同步 1.2.2Counte…...
WPF3-在xaml中引用其他程序集的名称空间
1. 如何在XAML里引用类库中的名称空间和类2. 小结 1. 如何在XAML里引用类库中的名称空间和类 首先需要记住一点:把类库引用到项目中是引用其中名称空间的物理基础,无论是C#还是XAML都是这样。 一旦将一个类库引用进程序,就可以引用其中的名…...
无人机红外热成像:应急消防的“透视眼”
无人机红外热成像:应急消防的“透视眼” 亲爱的小伙伴们,每年一到夏天,应急消防的战士们就像上紧了发条的闹钟,时刻准备应对各种灾害。炎热天气让火灾隐患“蹭蹭”往上涨,南北各地还有防洪救灾、台风、泥石流等灾害轮…...
飞牛NAS安装过程中的docker源问题
采用CloudFlare进行飞牛NAS的远程访问 【安全免费】无需公网IP、端口号,NAS外网访问新方法_网络存储_什么值得买 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1panel.dev&quo…...
python基础语法(4) ----- 学习笔记分享
目录 Python 使用库 以及 实战的一些案例 1. 标准库 1.1 认识标准库 1.2 使用import导入模块 1.3 代码示例:日期及结算 1.4 代码示例:字符串操作 1.5 代码示例 : 文件查找工具 2. 第三方库 2.1 认识第三方库 2.2 使用pip 2.3 代码示例:生成二维码 (1) 使用搜索引擎,…...
Linux 内核学习 3b - 和copilot 讨论pci设备的物理地址在内核空间和用户空间映射到虚拟地址的区别
前提知识: 可以把内核当作一个需要特殊权限运行的程序,那么不同的程序,相同的设备物理地址就会映射到不同的虚拟地址 (见Linux 内核学习 3 - 虚拟内存和物理内存)。 You said 同一个pcie 设备物理地址在linux 内核和用…...
算法【有依赖的背包】
有依赖的背包是指多个物品变成一个复合物品(互斥),每件复合物品不要和怎么要多种可能性展开。时间复杂度O(物品个数 * 背包容量),额外空间复杂度O(背包容量)。 下面通过题目加深理解。 题目一 测试链接:[NOIP2006 提…...
【Linux】 冯诺依曼体系与计算机系统架构全解
Linux相关知识点可以通过点击以下链接进行学习一起加油!初识指令指令进阶权限管理yum包管理与vim编辑器GCC/G编译器make与Makefile自动化构建GDB调试器与Git版本控制工具Linux下进度条 冯诺依曼体系是现代计算机设计的基石,其统一存储和顺序执行理念推动…...
【深度学习】 UNet详解
UNet 是一种经典的卷积神经网络(Convolutional Neural Network, CNN)架构,专为生物医学图像分割任务设计。该模型于 2015 年由 Olaf Ronneberger 等人在论文《U-Net: Convolutional Networks for Biomedical Image Segmentation》中首次提出&…...
“深入浅出”系列之算法篇:(2)openCV、openMV、openGL
OpenCV是一个的跨平台计算机视觉库,可以运行在Linux囚、Windows 和Mac OS操作系统上。它轻量级而且高效,由一系列 C函数和少量C类构成,同时也提供了Python 接口,实现了图像处理和计算机视觉方面的很多通用算法。 OpenMV是一个开源,低成本&am…...
低代码系统-产品架构案例介绍、得帆云(八)
产品名称 得帆云DeCode低代码平台-私有化 得帆云DeMDM主数据管理平台 得帆云DeCode低代码平台-公有云 得帆云DePortal企业门户 得帆云DeFusion融合集成平台 得帆云DeHoop数据中台 名词 概念 云原生 指自己搭建的运维平台,区别于阿里云、腾讯云 Dehoop 指…...
web3py+flask+ganache的智能合约教育平台
最近在学习web3的接口文档,使用web3pyflaskganache写了一个简易的智能合约教育平台,语言用的是python,ganche直接使用的本地区块链网络,用web3py进行交互。 代码逻辑不难,可以私信或者到我的闲鱼号夏沫mds获取我的代码…...
(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验六----流域综合处理(超超超详细!!!)
流域综合处理 流域综合治理是根据流域自然和社会经济状况及区域国民经济发展的要求,以流域水流失治理为中心,以提高生态经济效益和社会经济持续发展为目标,以基本农田优化结构和高效利用及植被建设为重点,建立具有水土保持兼高效生态经济功能的半山区流域综合治理模式。数字高程…...
基于 WPF 平台使用纯 C# 实现动态处理 json 字符串
一、引言 在当今的软件开发领域,数据的交换与存储变得愈发频繁,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,以其简洁、易读、便于解析和生成的特点,被广泛应用于各种应用程序中。在 W…...
DRF开发避坑指南01
在当今快速发展的Web开发领域,Django REST Framework(DRF)以其强大的功能和灵活性成为了众多开发者的首选。然而,错误的使用方法不仅会导致项目进度延误,还可能影响性能和安全性。本文将从我个人本身遇到的相关坑来给大…...
WordPress免费证书插件
为了在您的网站上启用HTTPS,您可以使用本插件快速获取Let’s Encrypt免费证书。 主要功能: 支持快速申请Let’s Encrypt免费证书支持通配符证书申请,每个证书最多可以绑定100个域名支持自动续期证书支持重颁发证书,证书过期或失…...
MongoDB平替数据库对比
背景 项目一直是与实时在线监测相关,特点数据量大,读写操作大,所以选用的是MongoDB。但按趋势来讲,需要有一款国产数据库可替代,实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…...
ANSYS学习笔记(十)网格质量的诊断和提高
网格质量的好坏不能单纯只看meshing给出的网格质量结果,要根据实际的计算物理场景来判断,需要求解的地方物理量大梯度的位置网格越密越好。 网格质量:在有限网格数量限制下,离散误差小的网格是好网格,是高质量网格。网…...
能量提升法三:赞美
前情回顾: 《能量提升法二:感恩》 片段:“感恩,就像是在跟世界说:谢谢你,我收到了,我很喜欢,请多来点” 把它归还人海,就当作每一个人,都有可能是曾经帮助…...
C++中函数返回值当引用
文章目录 一、概述二、返回值当引用的基本语法三、返回局部变量的引用四、返回引用的常见用途五、返回右值引用六、总结 一、概述 在 C 中,函数返回值当引用(即返回引用)是一个常见的编程技巧。它可以让你返回一个函数内部的局部变量或对象的…...
27. C语言 强制类型转换详解
本章目录: 前言强制类型转换(Type Casting)强制类型转换的语法示例1:将整数转换为浮点数输出结果: 代码解析: 整数提升(Integer Promotion)示例2:整数提升输出结果: 代码…...
Linux 命令之技巧(Tips for Linux Commands)
Linux 命令之技巧 简介 Linux 是一种免费使用和自由传播的类Unix操作系统,其内核由林纳斯本纳第克特托瓦兹(Linus Benedict Torvalds)于1991年10月5日首次发布。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户…...
freeswitch在centos上编译过程
操作系统:centos9-last usr/local/freeswitch/bin/freeswitch -version FreeSWITCH version: 1.10.13-devgit~20250125T131725Z~3f1e4bf90a~64bit (git 3f1e4bf 2025-01-25 13:17:25Z 64bit)vi /etc/ssh/sshd_config ip a nmtui reboot ip a curl -o /etc/pki/rpm-…...
快速入门Flink
Flink是新一代实时计算平台,采用原生的流处理系统,保证了低延迟性,在API和容错上也是做的相当完善,本文将从架构、组件栈、安装、入门程序等进行基础知识的分析,帮助大家快速对Flink有一个了解。 一.简介 1.是什么 Ap…...
c++ list
1.构造函数 构造函数 // list<T> lst; // list(beg, end); // 区间构造 // list(n, elem); // 元素构造 // list(const list &lst); // 拷贝构造#include <iostream> #include <fstream> #include <string> #include <list> using name…...
Vue 3 + TypeScript 实现父子组件协同工作案例解析
引言 在现代的前端开发中,Vue.js 作为一款流行的渐进式 JavaScript 框架,为我们构建交互式用户界面提供了强大的支持。Vue 3 的推出带来了许多新特性,尤其是组合式 API 的引入,让代码的组织和复用更加灵活。同时,TypeS…...
深度剖析C++17中的std::optional:处理可能缺失值的利器
文章目录 一、基本概念与设计理念二、构建与初始化(一)默认构造(二)值初始化(三)使用std::make_optional(四)使用std::nullopt 三、访问值(一)value()&#x…...
【ArcGIS微课1000例】0141:提取多波段影像中的单个波段
文章目录 一、波段提取函数二、加载单波段导出问题描述:如下图所示,img格式的时序NDVI数据有24个波段。现在需要提取某一个波段,该怎样操作? 一、波段提取函数 首先加载多波段数据。点击【窗口】→【影像分析】。 选择需要处理的多波段影像,点击下方的【添加函数】。 在多…...
一分钟搭建promehteus+grafana+alertmanager监控平台
为什么要自己搭建一个监控平台 平时进行后端开发,特别是微服务的后端可开发,一定少不了对接监控平台,但是平时进行一些小功能的测试又没有必要每次都手动安装那么多软件进行一个小功能的测试,这里我使用docker-compose搭建了一个…...
Transfoemr的解码器(Decoder)与分词技术
在自然语言处理(NLP)领域,解码器(Decoder)和分词技术是两个至关重要的概念。解码器是序列生成任务的核心组件,而分词则是将文本数据转换为可处理形式的基础步骤。 一、解码器(Decoder&…...
LeetCode100之在排序数组中查找元素的第一个和最后一个位置(34)--Java
1.问题描述 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题 示例1 输入…...
数字人+展厅应用方案:开启全新沉浸式游览体验
随着人们生活质量的不断提升,对于美好体验的追求日益增长。在展厅展馆领域,传统的展示方式已难以满足大众日益多样化的需求。而通过将数字人与展厅进行深度结合,可以打造数字化、智能化新型展厅,不仅能提升展示效果,还…...
echo ‘export PATH=/usr/local/bin:$PATH‘ >> ~/.bashrc这个和直接添加到/etc/profile有什么区别
echo export PATH/usr/local/bin:$PATH >> ~/.bashrc 和直接添加到 /etc/profile 都是用于修改 PATH 环境变量,但它们适用的范围和效果有所不同: 1. 修改 ~/.bashrc 文件 作用范围:~/.bashrc 是针对当前用户的配置文件,它…...
kafka消费者详细介绍(超级详细)
文章目录 一、Kafka 消费者与消费者组1.1 Kafka 消费者(Consumer)概述1.1.1 消费者工作流程1.1.2 消费者的关键配置 1.2 Kafka 消费者组(Consumer Group)概述1.2.1 消费者组的工作原理1.2.2 消费者组的优点1.2.3 消费者组的再均衡…...
《剪映5.9官方安装包》免费自动生成字幕
(避免失效建议存自己网盘后下载)剪映5.9官方Win.Mac 链接:https://pan.xunlei.com/s/VOHc-Fg2XRlD50MueEaOOeW1A1?pwdawtt# 官方唯一的免费版,Win和Mac都有,此版本官方已下架,觉得有用可转存收藏…...
CAS是什么?ABA会带来什么影响?怎么解决ABA问题?
前言 在高并发开发中,CAS(比较并交换)是一种常用的无锁操作,因其高效性而被广泛应用。然而,实际工作中常会遇到ABA问题,导致数据更新异常或逻辑错误。理解CAS的原理及ABA问题的解决方法,有助于…...
智能调度体系与自动驾驶技术优化运输配送效率的研究——兼论开源AI智能名片2+1链动模式S2B2C商城小程序的应用潜力
摘要:随着全球化和数字化进程的加速,消费者需求日益呈现出碎片化和个性化的趋势,这对物流运输行业提出了前所未有的挑战。传统的物流调度体系与调度方式已难以满足当前复杂多变的物流需求,因此,物流企业必须积极引入大…...
方豆子(递归)
方豆子 思路:很典的一道递归题,但当时没想到怎么递归/(ㄒoㄒ)/~~。赛后看了大佬的讲解知道要将这个图形看成由四个小正方形组成的大正方形,递归参数可以设置成(r1,c1,r2,c2,good)表示正方形的左上角坐标和右下角坐标以及当前这个正…...
Go语言入门指南(二): 数据类型
文章创作不易,麻烦大家点赞关注转发一键三连。 在上一篇文章,我们已经完成了开发环境的搭建,成功创建了第一个“Hello, World”程序,并且对变量的声明和初始化有了初步的认识。在这篇文章中,我们将主要介绍Go语言的数据…...
Django ORM解决Oracle表多主键的问题
现状 以Django 3.2为例 Django ORM 设计为默认使用单一主键(通常是自增的 id 字段),这一选择主要基于以下核心原因: 简化ORM设计与操作 统一访问方式外键关联简化 避免歧义冲突 主键语义明确防止隐式依赖 性能与数据库兼容 索引…...
学习数据结构(2)空间复杂度+顺序表
1.空间复杂度 (1)概念 空间复杂度也是一个数学表达式,表示一个算法在运行过程中根据算法的需要额外临时开辟的空间。 空间复杂度不是指程序占用了多少bytes的空间,因为常规情况每个对象大小差异不会很大,所以空间复杂…...
实验一---典型环节及其阶跃响应---自动控制原理实验课
一 实验目的 1.掌握典型环节阶跃响应分析的基本原理和一般方法。 2. 掌握MATLAB编程分析阶跃响应方法。 二 实验仪器 1. 计算机 2. MATLAB软件 三 实验内容及步骤 利用MATLAB中Simulink模块构建下述典型一阶系统的模拟电路并测量其在阶跃响应。 1.比例环节的模拟电路 提…...
从零推导线性回归:最小二乘法与梯度下降的数学原理
欢迎来到我的主页:【Echo-Nie】 本篇文章收录于专栏【机器学习】 本文所有内容相关代码都可在以下仓库中找到: Github-MachineLearning 1 线性回归 1.1 什么是线性回归 线性回归是一种用来预测和分析数据之间关系的工具。它的核心思想是找到一条直…...
OpenSIPS-从安装部署开始认识一个组件
前期讲到了Kamailio,它是一个不错的开源SIP(Session Initiation Protocol)服务器,主要用于构建高效的VoIP(Voice over IP)平台以及即时通讯服务。但是在同根同源(OpenSER)的分支上&a…...
数据结构(树)
每一个节点包含:父节点地址 值 左子节点地址 右子节点地址 如果一个节点不含有:父节点地址或左子节点地址 右子节点地址就记为null 二叉树 度:每一个节点的子节点数量 二叉树中,任意节点的度<2 树的结构: 二叉查…...
[Dialog屏幕开发] 设置搜索帮助
阅读该篇文章之前,可先阅读下述资料 [Dialog屏幕开发] 屏幕绘制(使用向导创建Tabstrip Control标签条控件)https://blog.csdn.net/Hudas/article/details/145372195?spm1001.2014.3001.5501https://blog.csdn.net/Hudas/article/details/145372195?spm1001.2014.…...