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

游戏编程模式学习(编程质量提升之路)

文章目录

  • 前言
  • 一、命令模式(Command Pattern)
    • 1.命令模式练习场景
      • I.需求场景
    • 2.解耦命令与执行者
    • 3.使用命令对玩家角色和AI的操作进行统一抽象
    • 4. 命令模式的撤销实现
  • 二、享元模式
    • 1.应用场景
    • 2.目的
    • 3.实现方式
  • 三、原型模式
    • 1.运用场景
    • 2.实现方式
  • 四、状态模式
    • 1.实现轻量级的状态机
  • 五、双缓冲模式
    • 1.使用场景

前言

在稍微学习游戏编程常用的设计模式,用于到框架设计当中,再进一步替身编程质量
文档地址:https://gameprogrammingpatterns.com/
中文地址:https://gpp.tkchu.me/

一、命令模式(Command Pattern)

将一个请求(request)封装成一个对象,从而允许你使用不同的请求,队列或者日志将客户端参数化,同时支持请求操作的撤销和恢复。

命令就是一个对象化(实例化)的方法调用。

补充:
commands are an object-oriented replacement for acallbacks.
命令是面对对象化的回调。

注解:无论使用哪种方法阐述,命令相当于对函数(或者叫方法、回调、行为、DoSomeThing)的一种延伸和封装,试之具备良好的扩展性。

1.命令模式练习场景

I.需求场景

实例-输入响应。四个键对应角色的不同行为。
基础是实现代码如下:

void InputHanlder(){if(Input.GetKeyCode(KeyCode.X)) jump();else if(Input.GetKeyCode(KeyCode.Y)) fireGun();else if(Input.GetKeyCode(KeyCode.A)) swapWeapon();else if(Input.GetKeyCode(KeyCode.B)) lurchIneffecively();
}

上面代码是将所有检测按钮命令的代码写进到了InputHandler里面。而如果使用基础的命令模式来实现,如下创建一个Command类

class Command{ public virtual void execute(){};
}

创建各个子类

class JumpCommand : Command{ public override void execute(){jump();}
}class FireCommand : Command{ public override void execute(){swapWeapon();}
}class SwapWeaponCommand : Command{ public override void execute(){fireGun();}
}class LurchIneffecivelyCommand : Command{ public override void execute(){lurchIneffecively();}
}

这样通过各自Command的子类来实现对应的操作。

管理器中存储基类,实例化为不同的子类,输入通过实例进行处理

class InputHandler{private Command buttonX;private Command buttonY;private Command buttonA;private Command buttonB;public void handlerInput(){if(Input.GetKeyCode(KeyCode.X))   buttonX.execute();else if(Input.GetKeyCode(KeyCode.Y)) buttonY.execute();else if(Input.GetKeyCode(KeyCode.A))  buttonA.execute();else if(Input.GetKeyCode(KeyCode.B))  buttonB.execute();}
}

补充:使用空对象模式来处理null,可使用工厂模式来完成空对象的创建。

再创建一个简单的Factory类型来对InputHandler对象赋值

public static class CommandFactory{public static Command Create(KeyCode keycode){switch(keycode){case X: return new JumpCommand();case Y: return new FireCommand();case A: return new SwapWeaponCommand();case B: return new LurchIneffecivelyCommand();default:throw new InuptOutOfRangeException("error keycode!");}}
}

2.解耦命令与执行者

加入我们要具体实现jump方法的功能的时候,这里是跳跃的指令,所以把跳跃的执行者依赖到JumpCommand当中会有一个问题,因为Command父类里面没有获取JumpBehavior的方法,缺乏内聚性,也违反了开闭原则。

class JumpCommand : Command{ public JumpBehavior Behavor;public override void execute(){ Behavor.Jump();}
}

应该将命令和执行者进行解耦,当然执行者本身也需要进行一下抽象,代码如下:

class JumpCommand : Command{  public override void execute(IBehavior behavor){ behavor.Jump();}
}interface IBehavior{public void Jump();
}

通过传入参数的方式来减少类的依赖。进行execute时将对应要执行命令的执行者传入过来。

3.使用命令对玩家角色和AI的操作进行统一抽象

对AI角色使用同样命令操作,输入命令的实现交予AI【可使用状态机,行为树等】
通过命令流实现生产者端-消费者端解耦(人工智能->命令流->角色)

首先需要实现一个AIAtcor,而这个AIActor本身是执行者,所以继承IBehavior

public class AIActor : IBehavior{private ICommand[] Commaands;private float stayTime;private ICommand lastCommand;private Command JumpCommand;private Command FireCommand; protected void ICommand HandleInput(){if(stayTime > 0){stayTime -= Time.deltaTime;return lastCommand;}stayTime = Random.Range(0,2);if(Commands == null){Commands = new ICommand[]{JumpCommand,FireCommand};}int rand = Random.Range(0,2);lastCommand = Commands[rand];return lastCommand;}
} 

4. 命令模式的撤销实现

原理是如果一个命令对象能做一件事情,那么这件事情是可以撤销的。

实现方法,提取命令:
1.注意保存具体的信息,而不是统一的行为。
2.此时的命令不是重复执行的对象,而是根据实际情况,在所需的时机不断创建的实力。

定义并实现Undo

//假设操作是一个简单的移动操作
void Undo(){MoveTo(Vector3 before){//执行前保存下before的}
}
class MoveCommand : Command{private Vector3 currectState;private Vector3 before;private Vector3 nextState;public MoveCommand(Vector3 state,Vector3 nextState){currectState = state;nextState = nextState;}public void Excute(IBehavior behavor){before = currectState;behavor.MoveTo(nextState);}public void Undo(IBehavior behavor){currectState = before;behavor.moveTo(before);}
}

这里是保存before状态,回退的核心。多条撤销与重做的情况下,需要用数据结构List保存命令
Undo=>回退上一条,并前往移动前保存的位置before
Redo=>执行当前标志位的下一位,并且在把标志位指向下一位
这里编写一下代码

补充,实现记录的方法有两种
1.备忘录模式,即记录快照(常用设计方式,数据在命令中)
2.持久化数据,即保存新的实体对象(每次创建新的命令,都创建一个新的命令执行对象的主体。数据都在执行者身上)

二、享元模式

1.应用场景

I.大量相同相似的对象
II.可按内容状态分组,并将不同的部分外部化
III.享元实例足够多的情况下才有必要

2.目的

I.减少大量内存使用
II.降低程序需要处理的数据量
III.创造需要共享信息的大量实例更加方便

3.实现方式

设计数据结构,使之包含共享部分。对象非共享部分额外存储,对象实例引用共享部分,不对其进行修改。

这里使用Unity的ScriptableObject举例子,简单创建下结构

[Serializable]
public struct ObjectFlyweightData{public Mesh mesh;public Material[] materials;
}[Serializable]
public struct SceneObject{public ObjectFlyweightData flyWeightData;public string name;public Matrix4x4 localToWrold;
}public class SceneObjectGroup : ScriptableObject{public SceneObject[] SceneObject;
}

上面比如说,我们在场景中有很多对象,而场景中的使用相同的网格和材质的东西可以作为享元部分。

三、原型模式

1.运用场景

当对象有一些比较通用的状态时,可以考虑从原型模式开始构建新的对象,从而避免从零开始构建新的对象。

从零开始构建的缺点:
几乎没有进行复用,造成浪费
结构相似,内部状态接近,仅有部分不同的物件,构建逻辑重复度高

2.实现方式

步骤一、对原型的全部状态进行克隆(根据情况进行深度克隆还是浅度克隆)
步骤二、后续对与原型不同的状态进行修改,重新赋值

实现途径有多种
1.原型类上面实现clone方法
2.使用构建者Spawner来实现

这里实现一个原型生成器

public class PrototypeSpawner : MonoBehaviour{public GameObject Spawn(GameObject prototypeObject,PrototypeSpawnAdditionalDataGroup additionalData){var target = Insrantiate(prototypeObject);if(additionalData!=null){additionalData.AttachTo(target);}return target;}
}public class PrototypeSpawnAdditionalDataGroup : ScriptableObject{public PositionAdditionData Postion;public MainMaterialReplaceAddition MainMaterial;public ArmScaleAddition ArmScale;public void AttachTo(GameObject target){Postion?.AttachTo(target);MainMaterial?.AttachTo(target);ArmScale?.AttachTo(target);}
}

上面代码是把prototypeObject作为原型,PrototypeSpawnAdditionalDataGroup里面是作为不同原型的修改的数据。

四、状态模式

假设我们在完成一个卷轴平台游戏。 现在的工作是实现玩家在游戏世界中操作的女英雄。 这就意味着她需要对玩家的输入做出响应。按B键她应该跳跃。简单实现如下:

public class Heroine : MonoBehavior{void Update(){if(Inuput.GetKey(KeyCode.B)){Jump();}}
}

看到漏洞了吗?
没有东西阻止“空中跳跃”——当角色在空中时狂按B,她就会浮空。 简单的修复方法是给Heroine增加isJumping_布尔字段,追踪它跳跃的状态。然后这样做:

public class Heroine : MonoBehavior{bool isJumping = false;void Update(){if(Inuput.GetKey(KeyCode.B)){if(!isJumping){Jump();}}}
}

接下来,当玩家按下下方向键时,如果角色在地上,我们想要她卧倒,而松开按键时站起来:

public class Heroine : MonoBehavior{bool isJumping = false;void Update(){if(Inuput.GetKey(KeyCode.B)){if(!isJumping && !isDucking){Jump();}}else if(Input.GetKey(KeyCode.Down)){if(isDucking){Duck();}}}
}

这样进行开发,我们会发现,每次添加新的功能都要添加多一个bool值来进行判断和约束,当功能越来越多就会bool的数量就指数增加,那么代码的复杂程度会指数级别增加。

这个时候是应该使用状态机的形式来进行开发。
在这里插入图片描述
祝贺,你刚刚建好了一个有限状态机。 它来自计算机科学的分支自动理论,那里有很多著名的数据结构,包括著名的图灵机。 FSMs是其中最简单的成员。

要点是:
你拥有状态机所有可能状态的集合。 在我们的例子中,是站立,跳跃,俯卧和速降。
状态机同时只能在一个状态。 英雄不可能同时处于跳跃和站立状态。事实上,防止这点是使用FSM的理由之一。
一连串的输入或事件被发送给状态机。 在我们的例子中,就是按键按下和松开。
每个状态都有一系列的转移,每个转移与输入和另一状态相关。 当输入进来,如果它与当前状态的某个转移相匹配,机器转换为所指的状态。

举个例子,在站立状态时,按下下方向键转换为俯卧状态。 在跳跃时按下下方向键转换为速降。 如果输入在当前状态没有定义转移,输入就被忽视。

1.实现轻量级的状态机

首先先对状态机进行抽象化

public interface IFsm{int CurrentState {get;}//添加对应的状态码,设置进入、退出、更新状态的时候调用的回调。bool AddState(int state,Action<int> onEnter,Action<int> onExit,Action<float> onUpdate);//删除掉这个状态bool RemoveState(int state);//更新函数bool Update(float time);//添加从状态from到to使用什么触发码bool AddTransiton(int from,int to,int triggerCode);//触发一下对应时间bool TriggerEvent(int eventCode);//状态切换bool SwitchToState(int stateId,bool forceSwitch);
}public class LightFsm : IFsm{private readonly Dictnary<int,Tuple<Action<int>,Action<int>,Action><float>> actions = new ();public int CurrentState{get;private set;}private Action<float> beforeUpdateCallBack;private Action<float> afterUpdateCallBack;private List<ValueTple<int,int,int>> transitions = new ();public LightFsm(int defaultState = -1,Action<float> beforeUpdate,Action<float> afterUpdate){CurrentState = defaultState ;beforeUpdateCallBack = beforeUpdate;afterUpdateCallBack = afterUpdate;}public bool AddState(int state,Action<int> onEnter,Action<int> onExit,Action<float> onUpdate){if(actions.containKey(state)){throw new Exception($"不能重复添加状态{state}行为");}actions.Add(state,new Tuple<Action<int>,Action<int>,Action><float>>(onEnter,onExit,onUpdate));return true;}public bool AddTransition(int from,int to,triggerCode){if(!actions.containKey(from) || !actions.containKey(to)){return false;}transitions.Add((from,to,triggerCode));return true;}public bool TriggerEvent(int eventCode){foreach(var transition in transitions){if(transition.Item1 == CurrentState && transition.Item3 == eventCode){SwitchToState(transition.Item2);return true;}}return false;}public bool AddState(int state){if(actions.ContainKey(state)){ actions.Remove(state);return true;} return false;}public bool SwicthToState(int stateId,bool forceSwitch = false){bool hasState = action.ContainKey(stateId);if(!hasState) return false;bool stateChanged = stateId != CurrectState;if(!stateChnaged && !forceSwitch) return false;if(stateChanged){if(actions.TryGetValue(CurrentState,out var oldActions)){oldActions.Item2?.Invoke(stateId);}}var oldStateId = CurrentState;var newActions = action[stateId];CurrentState = stateId;newActions.Item1?Invoke(oldStateId);return true;}public void Update(float time){if(actions.TryGetValue(CurrentState,out var actions)){beforeUpdateCallBack?Invoke(time);actions.Item3?.Invoke(time);afterUpdateCallBack?Invoke(time);}}
}

五、双缓冲模式

电脑具有强大的序列化处理能力。 它的力量来自于将大的任务分解为小的步骤,这样可以一步接一步的完成。 但是,通常用户需要看到事情发生在瞬间或者让多个任务同时进行。

使用线程和多核架构让这种说法不那么正确了,但哪怕使用多核,也只有一些操作可以同步运行。

一个典型的例子,也是每个游戏引擎都得掌控的问题,渲染。 当游戏渲染玩家所见的世界时,它同时需要处理一堆东西——远处的山,起伏的丘陵,树木,每个都在各自的循环中处理。 如果在用户观察时增量做这些,连续世界的幻觉就会被打破。 场景必须快速流畅地更新,显示一系列完整的帧,每帧都是立即出现的。

双缓冲解决了这个问题,但是为了理解其原理,让我们首先的复习下计算机是如何显示图形的。

计算机图形系统是如何工作的(概述)
在电脑屏幕上显示图像是一次绘制一个像素点。 它从左到右扫描每行像素点,然后移动至下一行。 当抵达了右下角,它退回左上角重新开始。 它做得飞快——每秒六十次——因此我们的眼睛无法察觉。 对我们来说,这是一整张静态的彩色像素——一张图像。

这个解释是“简化过的”。 如果你是底层软件开发人员,跳过下一节吧。 你对这章的其余部分已经了解得够多了。 如果你不是,这部分的目标是给你足够的背景知识,理解等下要讨论的设计模式。

你可以将整个过程想象为软管向屏幕喷洒像素。 独特的像素从软管的后面流入,然后在屏幕上喷洒,每次对一个像素涂一点颜色。 所以软管怎么知道哪种颜色要喷到哪里?

在大多数电脑上,答案是从帧缓冲中获知这些信息。 帧缓冲是内存中的色素数组,RAM中每两个字节代表表示一个像素点的颜色。 当软管向屏幕喷洒时,它从这个数组中读取颜色值,每次一个字节。

在字节值和颜色之间的映射通常由系统的像素格式和色深来指定。 在今日多数游戏主机上,每个像素都有32位,红绿蓝三个各占八位,剩下的八位保留作其他用途。

最终,为了让游戏显示在屏幕中,我们需要做的就是写入这个数组。 我们疯狂摆弄的图形算法最终都到了这里:设置帧缓冲中的字节值。 但这里有个小问题。

早先,我说过计算机是顺序处理的。 如果机器在运行一块渲染代码,我们不指望它同时还能做些别的什么事。 这通常是没啥问题,但是有些事确实在程序运行时发生。 其中一件是,当游戏运行时,视频输出正在不断从帧缓冲中读取数据。 这可能会为我们带来问题。

假设我们要在屏幕上显示一张笑脸。 程序在帧缓冲上开始循环,为像素点涂色。 我们没有意识到的是,在写入的同时,视频驱动正在读取它。 当它扫描过已写的像素时,笑脸开始浮现,但是之后它进入了未写的部分,就将没有写的像素绘制到了屏幕上。结果就是撕裂,你在屏幕上看到了绘制到一半的图像,这是可怕的视觉漏洞。
在这里插入图片描述
这就是我们需要这个设计模式的原因。 程序一次渲染一个像素,但是显示需要一次全部看到——在这帧中啥也没有,下一帧笑脸全部出现。 双缓冲解决了这个问题。我会用类比来解释。

表演1,场景1
想象玩家正在观看我们的表演。 在场景一结束而场景二开始时,我们需要改变舞台设置。 如果让场务在场景结束后进去拖动东西,场景的连贯性就被打破了。 我们可以减弱灯光(这是剧院实际上的做法),但是观众还是知道有什么在进行,而我们想在场景间毫无跳跃地转换。

通过消耗一些地皮,我们想到了一个聪明的解决方案:建两个舞台,观众两个都能看到。 每个有它自己的一组灯光。我们称这些舞台为舞台A和舞台B。 场景一在舞台A上。同时场务在处于黑暗之中的舞台B布置场景二。 当场景一完成后,将切断场景A的灯光,打开场景B的灯光。观众看向新舞台,场景二立即开始。

同时,场务到了黑咕隆咚的舞台A,收拾了场景一然后布置场景三。 一旦场景二结束,将灯光转回舞台A。 我们在整场表演中进行这样的活动,使用黑暗的舞台作为布置下一场景的工作区域。 每一次场景转换,只是在两个舞台间切换灯光。 观众获得了连续的体验,场景转换时没有感到任何中断。他们从来没有见到场务。

使用单面镜以及其他的巧妙布置,你可以真正地在同一位置布置两个舞台。 随着灯光切换,观众看到了不同的舞台,无需看向不同的地方。 如何这样布置舞台就留给读者做练习吧。

重新回到图形
这就是双缓冲的工作原理, 这就是你看到的几乎每个游戏背后的渲染系统。 不只用一个帧缓冲,我们用两个。其中一个代表现在的帧,即类比中的舞台A,也就是说是显卡读取的那一个。 GPU可以想什么时候扫就什么时候扫。

但不是所有的游戏主机都是这么做的。 更老的简单主机中,内存有限,需要小心地同步绘制和渲染。那很需要技巧。

同时,我们的渲染代码正在写入另一个帧缓冲。 即黑暗中的舞台B。当渲染代码完成了场景的绘制,它将通过交换缓存来切换灯光。 这告诉图形硬件开始从第二块缓存中读取而不是第一块。 只要在刷新之前交换,就不会有任何撕裂出现,整个场景都会一下子出现。

这时可以使用以前的帧缓冲了。我们可以将下一帧渲染在它上面了。超棒!

模式
定义缓冲类封装了缓冲:一段可改变的状态。 这个缓冲被增量地修改,但我们想要外部的代码将修改视为单一的原子操作。 为了实现这点,类保存了两个缓冲的实例:下一缓冲和当前缓冲。

当信息从缓冲区中读取,它总是读取当前的缓冲区。 当信息需要写到缓存,它总是在下一缓冲区上操作。 当改变完成后,一个交换操作会立刻将当前缓冲区和下一缓冲区交换, 这样新缓冲区就是公共可见的了。旧的缓冲区成为下一个重用的缓冲区。

1.使用场景

这是那种你需要它时自然会想起的模式。 如果你有一个系统需要双缓冲,它可能有可见的错误(撕裂之类的)或者行为不正确。 但是,“当你需要时自然会想起”没提提供太多有效信息。 更加特殊地,以下情况都满足时,使用这个模式就很恰当:

我们需要维护一些被增量修改的状态。
在修改到一半的时候,状态可能会被外部请求。
我们想要防止请求状态的外部代码知道内部的工作方式。
我们想要读取状态,而且不想等着修改完成。

记住
不像其他较大的架构模式,双缓冲模式位于底层。 正因如此,它对代码库的其他部分影响较小——大多数游戏甚至不会感到有区别。 尽管这里还是有几个警告。

交换本身需要时间
在状态被修改后,双缓冲需要一个swap步骤。 这个操作必须是原子的——在交换时,没有代码可以接触到任何一个状态。 通常,这就是修改一个指针那么快,但是如果交换消耗的时间长于修改状态的时间,那可是毫无助益。
我们得保存两个缓冲区
这个模式的另一个结果是增加了内存的使用。 正如其名,这个模式需要你在内存中一直保留两个状态的拷贝。 在内存受限的设备上,你可能要付出惨痛的代价。 如果你不能接受使用两份内存,你需要使用别的方法保证状态在修改时不会被请求。

相关文章:

游戏编程模式学习(编程质量提升之路)

文章目录 前言一、命令模式&#xff08;Command Pattern&#xff09;1.命令模式练习场景I.需求场景 2.解耦命令与执行者3.使用命令对玩家角色和AI的操作进行统一抽象4. 命令模式的撤销实现 二、享元模式1.应用场景2.目的3.实现方式 三、原型模式1.运用场景2.实现方式 四、状态模…...

计算机视觉五大技术——深度学习在图像处理中的应用

深度学习是利用“多层神经网络”实现人工智能的一种方式 计算机视觉&#xff1a;“对图像中的客观对象构建明确而有意义的描述”&#xff0c;识别图片中的含义进行处理 1.图像分类——“图里有狗” 判断整张图片属于哪个类别&#xff0c;判断图片是“猫”还是“狗” 思路&a…...

Mixed Content: The page at https://xxx was loaded over HTTPS

一、核心原因分析 Mixed Content 警告是由于 HTTPS 页面中引用了 HTTP 协议的资源(如脚本、图片、iframe 等),导致浏览器因安全策略阻止加载这些非加密内容。HTTP 资源可能被中间人攻击篡改,破坏 HTTPS 页面的整体安全性。 二、推荐解决方案 1. 强制资源升级为 HTTPS •…...

transforms-pytorch4

数据通常不会直接是机器学习算法可以使用的“最终格式”。我们使用转换&#xff08;transforms&#xff09;来对数据进行处理&#xff0c;使其适合训练。 所有的 TorchVision 数据集都提供了两个参数&#xff1a;transform 用于修改特征&#xff0c;target_transform 用于修改…...

Springboot----@Role注解的作用

Role(BeanDefinition.ROLE_INFRASTRUCTURE) 是 Spring 框架中的一个注解&#xff0c;用于显式标记 Bean 的角色&#xff0c;表明该 Bean 是 Spring 容器内部的基础设施组件&#xff08;如后置处理器、工具类等&#xff09;&#xff0c;而非用户直接使用的业务 Bean。其核心作用…...

SpringBoot项目报错: 缺少 Validation

目录 为什么需要Validation&#xff1f;如何使用Validation&#xff1f; 缺少validation&#xff1f;这不过是代码的一个小小问题&#xff0c;就像被风带走的一片叶子&#xff0c;轻轻一吹就能解决啦&#xff01; 在你的项目中&#xff0c;如果你发现自己需要进行数据验证&…...

MySQL vs MSSQL 对比

在企业数据库管理系统中&#xff0c;MySQL 和 Microsoft SQL Server&#xff08;MSSQL&#xff09;是最受欢迎的两大选择。MySQL 是一款开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;由 MySQL AB 开发&#xff0c;现归属于 Oracle 公司。而 MSSQL 是微…...

预测分析(四):面向预测分析的神经网络简介

文章目录 面向预测分析的神经网络简介神经网络模型1. 基本概念2. 前馈神经网络3. 常见激活函数4. 循环神经网络&#xff08;RNN&#xff09;5. 卷积神经网络&#xff08;CNN&#xff09; MPL结构工作原理激活函数训练方法 基于神经网络的回归——以钻石为例构建预测钻石价格的M…...

实战交易策略 篇十四:江南神鹰捕捉热点和熊市生存交易策略

文章目录 系列文章捕捉热点是股市最大的掘金术市场温度不低于50是热点产生的必要条件题材的大小和新颖程度决定热点的持续时间和涨幅炒作热点的3个阶段捕捉热点的方法与步骤操作实战案例熊市生存术“熊市最好的做法是离开股市”的说法是一句空话熊市盈利模式:不轻言底部,超跌…...

去中心化衍生品(以Synthetix为例)

去中心化衍生品&#xff08;以Synthetix为例&#xff09; 核心概念 合成资产&#xff08;Synths&#xff09;&#xff1a; 定义&#xff1a;链上追踪现实资产价值的代币化合约&#xff08;如sXAU追踪黄金&#xff0c;iBTC反向追踪比特币&#xff09;。 类型&#xff1a; 正…...

JavaScript重难点突破:事件循环

想了解事件循环&#xff0c;首先要了解js中线程的概念。 宿主环境 在浏览器环境中&#xff0c;js实际上包含了三个部分ECMAScript、DOM&#xff08;文档对象模型&#xff09;、BOM&#xff08;浏览器对象模型&#xff09;&#xff0c;我们最熟悉的js代码指的是ECMAScript这一…...

Python每日一题(15)

Python每日一题2025.4.4 一、题目题目描述输入格式输出格式输入输出样例 #1输入 #1输出 #1 二、分析三、源代码四、deepseek 一、题目 题目描述 您需要写一种数据结构&#xff0c;来维护一些数&#xff08;都是绝对值 1 0 9 10^9 109 以内的数&#xff09;的集合&#xff0c…...

#SVA语法滴水穿石# (003)关于 sequence 和 property 的区别和联系

在 SystemVerilog Assertions (SVA) 中,sequence 和 property 是两个核心概念,它们既有区别又紧密相关。对于初学者,可能不需要过多理解;但是要想写出复杂精美的断言,深刻理解两者十分重要。今天,我们汇总和学习一下该知识点。 1. 区别 特性sequenceproperty定义描述一系…...

有人DTU使用MQTT协议控制Modbus协议的下位机-含数据库

本文为备忘录&#xff0c;不做太多解释。 DTU型号&#xff1a;G780 服务器&#xff1a;win2018 一。DTU设置 正确设置波特率&#xff0c;进入配置状态&#xff0c;获取当前参数&#xff0c;修改参数&#xff0c;设置并保存所有参数。 1.通道1设置 2.Modbus轮询设置 二&am…...

Smart Link 技术全面解析

1.1 网络冗余技术的演进与需求 1.2 Smart Link 的核心价值与本文目标 第一章 Smart Link 技术概述 2.1 Smart Link 的应用场景与背景 2.2 Smart Link 的基本概念与组网角色 2.3 Smart Link 与传统技术的对比 第二章 Smart Link 工作原理 3.1 Smart Link 组的构成与运行机…...

【学Rust写CAD】30 Alpha256结构体补充方法(alpha256.rs)

源码 impl Alpha256 {#[inline]pub fn alpha_mul(&self, x: u32) -> u32 {let mask 0xFF00FF;let src_rb ((x & mask) * self.0) >> 8;let src_ag ((x >> 8) & mask) * self.0;(src_rb & mask) | (src_ag & !mask)} }代码分析 功能 输…...

提升 Web 性能:使用响应式图片优化体验

在现代 Web 开发中&#xff0c;图片通常占据页面加载的大部分带宽&#xff0c;如何高效管理图片资源直接影响用户体验和性能得分。Google 的 Lighthouse 工具在性能审计中特别强调“使用响应式图片”&#xff08;Uses Responsive Images&#xff09;&#xff0c;旨在确保图片在…...

基于K8s的演示用单机ML服务部署

这是仅用一台机器&#xff08;比如一台MacBook&#xff09;模拟在k8s上部署一个机器学习服务的演示用实例。 项目地址&#xff1a;https://github.com/HarmoniaLeo/Local-K8s-ML-Demo 该实例分为以下几个部分&#xff1a; 使用KerasTensorflow搭建并训练神经网络&#xff0c…...

强化中小学人工智能教育:塑造未来社会的科技基石

在数字化浪潮席卷全球的今天,人工智能(AI)已成为推动社会进步与经济发展的核心力量。面对这一不可逆转的趋势,如何培养具备AI素养与创新能力的下一代,成为各国教育改革的重中之重。辽宁省教育厅近日发布的《关于加强中小学人工智能教育的实施方案》,无疑为我国中小学人工…...

音视频基础(视频的主要概念)

文章目录 **1. 视频码率&#xff08;Bitrate&#xff09;****概念****分类****码率对比** **2. 视频帧率&#xff08;Frame Rate, FPS&#xff09;****概念****常见帧率****帧率 vs. 观感** **3. 视频分辨率&#xff08;Resolution&#xff09;****概念****常见分辨率****分辨率…...

JWT与Session的实战选择-杂谈(1)

JWT与Session的实战选择&#xff1a;从原理到踩坑心得 作为在金融科技领域经历过多次认证方案迭代的开发者&#xff0c;我想分享一些实战经验。这两种方案适用场景各异&#xff0c;选型需慎重考量。 一、本质差异&#xff1a;状态管理方式 Session机制&#xff1a;服务端维护…...

SQL Server安装后 Reporting Services 配置失败

问题现象&#xff1a; 完成 SQL Server 2022 安装后&#xff0c;尝试配置 Reporting Services (SSRS) 时失败&#xff0c;错误提示 “报表服务器数据库配置无效” 或 “无法启动 Reporting Services 服务”&#xff08;错误代码 0x80070005&#xff09;。 快速诊断 检查服务状态…...

操作系统面经(一)

部分参考来自小林coding 线程、进程、协程 进程是操作系统分配资源&#xff08;内存、文件等&#xff09;的基本单位&#xff0c;每个进程独立运行&#xff0c;互相隔离&#xff0c;稳定性高但开销大&#xff1b;线程是CPU调度的基本单位&#xff0c;属于同一进程的多个线程共…...

Qt 中 findChild和findChildren绑定自定义控件

在 Qt 中&#xff0c;findChild 和 findChildren 是两个非常实用的方法&#xff0c;用于在对象树中查找特定类型的子对象。这两个方法是 QObject 类的成员函数&#xff0c;因此所有继承自 QObject 的类都可以使用它们。当您需要查找并绑定自定义控件时&#xff0c;可以按照以下…...

对模板方法模式的理解

对模板方法模式的理解 一、场景1、题目【[来源](https://kamacoder.com/problempage.php?pid1087)】1.1 题目描述1.2 输入描述1.3 输出描述1.4 输入示例1.5 输出示例 二、不采用模板方法模式1、代码2、问题 三、采用模板方法模式1、代码 四、总结 一、场景 1、题目【来源】 …...

SpringMVC+Spring+MyBatis知识点

目录 一、相关概念 1.关系 2.网页 3.架构 4.URL 5.http 6.https 7.服务器 8.Tomcat 9.Servelet 10.Javaweb作用域对象 11.JSP 二、相关操作 1.RequestDispatcher 2.sendRedirect 3.cookie 4.Session 5.Filter过滤器 6.Listener监听器 7.MVC模型 8.JDBC连接…...

程序化广告行业(58/89):系统架构与广告反作弊深度剖析

程序化广告行业&#xff08;58/89&#xff09;&#xff1a;系统架构与广告反作弊深度剖析 大家好&#xff01;在程序化广告这个充满挑战与机遇的领域&#xff0c;不断学习和探索是保持竞争力的关键。今天&#xff0c;我希望和大家一起学习进步&#xff0c;深入了解程序化广告行…...

一周学会Pandas2 Python数据处理与分析-NumPy简介

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili NumPy&#xff08;Numerical Python&#xff09;是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩…...

第二十七章:Python-Aquarel库与多种主题库结合实现Matplotlib美化

资源绑定附上完整资料供读者参考学习&#xff01; 一、库介绍与安装 1.1 Aquarel库 Aquarel是一个轻量级的Python库&#xff0c;用于简化Matplotlib的样式配置&#xff0c;使数据可视化更加美观和高效。 1.2 Catppuccin库 Catppuccin是一个社区驱动的粉彩主题库&#xff0…...

leetcode155.最小栈

思路源自 【力扣hot100】【LeetCode 155】最小栈 为了让检索时间达到o&#xff08;1&#xff09;&#xff0c;采用空间换时间&#xff0c;维护两个栈&#xff0c;第一个栈实现正常的push、pop、top&#xff0c;另一个栈的栈顶每次都只放以一个栈中最小的元素 class MinStack …...

Mysql 中的 MyISAM 引擎

&#x1f9f1; 什么是 MyISAM&#xff1f; MyISAM 是 MySQL 早期的默认存储引擎&#xff0c;特点是结构简单、读取速度快&#xff0c;但不支持事务和行级锁。 它适合那些 读多写少、对事务安全要求不高 的场景&#xff0c;比如旧版博客系统、数据仓库等。 &#x1f4e6; MyISA…...

操作系统、虚拟化技术与云原生及云原生AI简述

目录 操作系统基础 操作系统定义 操作系统的组成 操作系统的分类 Linux操作系统特性 虚拟化技术 概述 CPU虚拟化 内存虚拟化 I/O虚拟化 虚拟化技术 虚拟化平台管理工具 容器 容器与云原生&#xff1a;详细介绍 容器的特点 什么是云原生&#xff1f; 云原生的特点 容器与云原生的…...

Java EE期末总结(第二章)

目录 一、JSP页面里的page指令 二、JSP脚本元素 1、全局声明<%!……%> 2、表达式<%……%> 3、脚本程序段<%……%> 三、文件包含指令include 四、引入标签库指令taglib 五、JSP动作标签 1、包含文件动作标签 2、请求转发动作标签 3、JavaBean动作标签 …...

FreeRTOS任务查询和信息统计API

下面例举几个常见的任务查询API&#xff08;其余可参考FreeRTOS开发手册&#xff09;&#xff1a; UBaseType_t Priority; Priority uxTaskPriorityGet(QUERYTask_Handler); printf("Task Pri %d \r\n",Priority); TaskStatus_t * TaskStatusArray; UBaseType_t …...

SQLark(百灵连接):一款面向信创应用开发者的数据库开发和管理工具

SQLark&#xff08;百灵连接&#xff09;是一款面向信创应用开发者的数据库开发和管理工具&#xff0c;用于快速查询、创建和管理不同类型的数据库系统。 目前可以支持达梦数据库、Oracle 以及 MySQL。 SQL 智能编辑器 基于语法语义解析实现代码补全能力&#xff0c;为你提供…...

Linux | 安装超级终端串口软件连接i.MX6ULL开发板(8)

01 它的安装步骤也非常简单,安装语言选择中文简体,点击确定,如下图所示。 点击下一步,如下图所示。 02...

Qt 事件系统负载测试:深入理解 Qt 事件处理机制

Qt 事件系统负载测试&#xff1a;深入理解 Qt 事件处理机制 文章目录 Qt 事件系统负载测试&#xff1a;深入理解 Qt 事件处理机制摘要引言实现原理1. 自定义事件类型2. 事件队列管理3. 性能指标监控4. 事件发送机制 性能监控实现1. 负载计算2. 内存监控3. 延迟计算 使用效果优化…...

如何评价Manus?

Manus是由Monica公司发布的全球首款通用型AI Agent产品,定位于一个性能强大的通用型助手,能够独立思考、规划并执行复杂任务,直接交付完整的任务成果。‌‌ Manus 这个名字灵感来源于拉丁格言 “Mens et Manus”(意为“头脑与双手”),寓意它既能动脑也能动手。 Manus的核…...

AI浪潮下的IT职业转型:医药流通行业传统IT顾问的深度思考

AI浪潮下的IT职业转型&#xff1a;医药流通行业传统IT顾问的深度思考 一、AI重构IT行业的技术逻辑与实践路径 1.1 医药流通领域的智能办公革命 在医药批发企业的日常运营中&#xff0c;传统IT工具正经历颠覆性变革。以订单处理系统为例&#xff0c;某医药集团引入AI智能客服…...

2011-2019年各省地方财政国土资源气象等事务支出决策数数据

2011-2019年各省地方财政国土资源气象等事务支出决策数数据 1、时间&#xff1a;2007-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政国土资源气象等事务支出决策数 4、范围&#xff1a;31省 5、指标说明&#x…...

《微服务概念进阶》精简版

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 什么是微服务&#xff08;进阶精简版&#x…...

免费送源码:Java+SSM+Android Studio 基于Android Studio游戏搜索app的设计与实现 计算机毕业设计原创定制

摘要 本文旨在探讨基于SSM框架和Android Studio的游戏搜索App的设计与实现。首先&#xff0c;我们详细介绍了SSM框架&#xff0c;这是一种经典的Java Web开发框架&#xff0c;由Spring、SpringMVC和MyBatis三个开源项目整合而成&#xff0c;为开发企业级应用提供了高效、灵活、…...

栈回溯和离线断点

栈回溯和离线断点 栈回溯&#xff08;Stack Backtrace&#xff09; 栈回溯是一种重建函数调用链的技术&#xff0c;对于分析栈溢出的根本原因非常有价值。 实现方式 // 简单的栈回溯实现示例&#xff08;ARM Cortex-M架构&#xff09; void stack_backtrace(void) {uint32_…...

探索轻量高性能的 Rust HTTP 服务器框架 —— Hyperlane

探索轻量高性能的 Rust HTTP 服务器框架 —— Hyperlane 随着互联网应用对性能和实时性要求的不断提升&#xff0c;选择一个高效且易于扩展的 HTTP 服务器框架变得尤为重要。今天&#xff0c;我们将介绍一个专为 Rust 开发者设计的框架 —— Hyperlane。该框架不仅支持 HTTP 请…...

第四章 表单(3)- 表单验证

在Blazor中&#xff0c;表单的验证可以通过两种方式实现&#xff0c;一种是使用Blazor所提供表单验证特性&#xff0c;另一种是使用ValidationMessageStore进行验证。 表单验证的基础使用(内置特性) 一、内置特性表单验证的开启 Blazor中&#xff0c;使用表单组件<EditFo…...

手撕AVL树

引入&#xff1a;为何要有AVL树&#xff0c;二次搜索树有什么不足&#xff1f; 二叉搜索树有其自身的缺陷&#xff0c;假如往树中插入的元素有序或者接近有序&#xff0c;二叉搜索树就会退化成单支树&#xff0c;时间复杂度会退化成O(N)&#xff0c;因此产生了AVL树&#xff0c…...

Linux驱动开发练习案例

1 开发目标 1.1 架构图 操作系统&#xff1a;基于Linux5.10.10源码和STM32MP157开发板&#xff0c;完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪&#xff1b; 驱动层&#xff1a;为每个外设配置DTS并且单独封装外设驱动模块。其中电压ADC测试&#xff0c;采用linux内核…...

Redis 下载 — Ubuntu22.04稳定版,配置

官方文档 &#xff1a; https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/ Nano学习 &#xff1a; 【Linux环境下最先应该掌握的文本编辑器nano】https://www.bilibili.com/video/BV1p8411z7dJ?vd_source5ce003da2a16f44ea73ec9bbc30389e4 Redis配置…...

有没有可以帮助理解高数的视频或者书籍资料?

高数的学习是一个入门很高&#xff0c;但是一旦入门之后&#xff0c;就会变得比较简单的科目。 可是&#xff0c;我们应该怎么入门高数呢&#xff1f;在当年刚开始学习高数的时候&#xff0c;我也有过这样的困惑。 但是&#xff0c;后来我发现&#xff0c;我总是可以在经历一…...

了解拦截器

目录 什么是拦截器 拦截器的基本使用 拦截器的使用步骤 拦截器路径设置 拦截器执行流程 一、什么是拦截器 拦截器是Spring框架提供的核心功能之一&#xff0c;主要用来拦截用户的请求&#xff0c;在指定方法前后&#xff0c;根据业务需要执行预先设定的代码。 开发人员可以…...