UE4运用C++和框架开发坦克大战教程笔记(十二)(第37~39集)

UE4运用C++和框架开发坦克大战教程笔记(十二)(第37~39集)

  • 37. 延时事件系统
  • 38. 协程逻辑优化更新
  • 39. 普通按键绑定

37. 延时事件系统

由于梁迪老师是写 Unity 游戏出身的,所以即便 UE4 有自带的 TimeManager 这样的延时系统,老师还是重新写了一个符合 Unity 开发习惯的延时系统。

在 DDTypes 里定义延时任务结构体,以及它要用到的一个委托。

DDTypes.h

#pragma region InvokeDECLARE_DELEGATE(FDDInvokeEvent)struct DDInvokeTask
{// 延迟执行的时间float DelayTime;// 是否循环bool IsRepeat;// 循环时间间隔float RepeatTime;// 是否在循环阶段bool IsRepeatState;// 计时器float TimeCount;// 方法委托FDDInvokeEvent InvokeEvent;// 构造函数DDInvokeTask(float InDelayTime, bool InIsRepeat, float InRepeatTime){DelayTime = InDelayTime;IsRepeat = InIsRepeat;RepeatTime = InRepeatTime;IsRepeatState = false;TimeCount = 0.f;}// 帧更新操作函数bool UpdateOperate(float DeltaSeconds){TimeCount += DeltaSeconds;// 如果不循环的,到时间了执行一次就停止;否则执行一次后开启循环状态if (!IsRepeatState) {if (TimeCount >= DelayTime) {InvokeEvent.ExecuteIfBound();TimeCount = 0.f;if (IsRepeat)IsRepeatState = true;elsereturn true;}} else {if (TimeCount >= RepeatTime) {InvokeEvent.ExecuteIfBound();TimeCount = 0.f;}}return false;}
};#pragma endregion

我们依旧将延时系统放在 DDMessage 这里。

协程系统和延时系统的 3 个方法的逻辑几乎都是一样的,所以可以依葫芦画瓢地将代码复制一份过来然后更改。

DDMessage.h

public:// 开始一个延时方法,返回 true 说明成功;返回 false 说明已经存在同对象名同任务名的任务bool StartInvoke(FName ObjectName, FName InvokeName, DDInvokeTask* InvokeTask);// 停止一个延时bool StopInvoke(FName ObjectName, FName InvokeName);// 停止某对象下的所有延时方法void StopAllInvoke(FName ObjectName);protected:// 延时序列,第一个 FName 是对象名,第二个 FName 是延时任务名TMap<FName, TMap<FName, DDInvokeTask*>> InvokeStack;

DDMessage.cpp

void UDDMessage::MessageTick(float DeltaSeconds)
{// 处理延时系统CompleteTask.Empty();	// 跟协程系统共用这个名字数组,所以要先清空for (TMap<FName, TMap<FName, DDInvokeTask*>>::TIterator It(InvokeStack); It; ++It) {TArray<FName> CompleteNode;	// 保存完成的延时任务名字for (TMap<FName, DDInvokeTask*>::TIterator Ih(It->Value); Ih; ++Ih) {if (Ih->Value->UpdateOperate(DeltaSeconds)) {delete Ih->Value;CompleteNode.Push(Ih->Key);}}for (int i = 0; i < CompleteNode.Num(); ++i)It->Value.Remove(CompleteNode[i]);if (It->Value.Num() == 0)CompleteTask.Push(It->Key);}for (int i = 0; i < CompleteTask.Num(); ++i) InvokeStack.Remove(CompleteTask[i]);
}bool UDDMessage::StartInvoke(FName ObjectName, FName InvokeName, DDInvokeTask* InvokeTask)
{if (!InvokeStack.Contains(ObjectName)) {TMap<FName, DDInvokeTask*> NewTaskStack;InvokeStack.Add(ObjectName, NewTaskStack);}if (!(InvokeStack.Find(ObjectName)->Contains(InvokeName))) {InvokeStack.Find(ObjectName)->Add(InvokeName, InvokeTask);return true;}delete InvokeTask;return false;
}bool UDDMessage::StopInvoke(FName ObjectName, FName InvokeName)
{if (InvokeStack.Contains(ObjectName) && InvokeStack.Find(ObjectName)->Find(InvokeName)) {DDInvokeTask* InvokeTask = *(InvokeStack.Find(ObjectName)->Find(InvokeName));InvokeStack.Find(ObjectName)->Remove(InvokeName);if (InvokeStack.Find(ObjectName)->Num() == 0)InvokeStack.Remove(ObjectName);delete InvokeTask;return true;}return false;
}void UDDMessage::StopAllInvoke(FName ObjectName)
{if (InvokeStack.Contains(ObjectName)) {for (TMap<FName, DDInvokeTask*>::TIterator It(*InvokeStack.Find(ObjectName)); It; ++It)delete It->Value;InvokeStack.Remove(ObjectName);}
}

调用路线依旧是 DDMessage – DDModule – DDOO – 对象,所以补充完整这条调用链。

DDModule.h

public:// 开始一个延时方法,返回 true 说明成功;返回 false 说明已经存在同对象名同任务名的任务bool StartInvoke(FName ObjectName, FName InvokeName, DDInvokeTask* InvokeTask);// 停止一个延时bool StopInvoke(FName ObjectName, FName InvokeName);// 停止某对象下的所有延时方法void StopAllInvoke(FName ObjectName);

DDModule.cpp

bool UDDModule::StartInvoke(FName ObjectName, FName InvokeName, DDInvokeTask* InvokeTask)
{return Message->StartInvoke(ObjectName, InvokeName, InvokeTask);
}bool UDDModule::StopInvoke(FName ObjectName, FName InvokeName)
{return Message->StopInvoke(ObjectName, InvokeName);
}void UDDModule::StopAllInvoke(FName ObjectName)
{Message->StopAllInvoke(ObjectName);
}

在 DDOO 里,需要将延时运行和延时循环运行分为两个方法。其余方法则跟协程系统一样只传递调用即可。

DDOO.h

protected:// 延时运行template<class UserClass>bool InvokeDelay(FName InvokeName, float DelayTime, UserClass* UserObj, typename FDDInvokeEvent::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod);// 延时循环运行,与上面这个方法的区别就是多传了一个循环间隔时长的 float 变量template<class UserClass>bool InvokeRepeat(FName InvokeName, float DelayTime, float RepeatTime, UserClass* UserObj, typename FDDInvokeEvent::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod);// 关闭延时方法bool StopInvoke(FName InvokeName);// 关闭对象下所有延时方法void StopAllInvoke();
};template<class UserClass>
bool IDDOO::InvokeDelay(FName InvokeName, float DelayTime, UserClass* UserObj, typename FDDInvokeEvent::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod)
{DDInvokeTask* InvokeTask = new DDInvokeTask(DelayTime, false, 0.f);InvokeTask->InvokeEvent.BindUObject(UserObj, InMethod);		// 绑定委托return IModule->StartInvoke(GetObjectName(), InvokeName, InvokeTask);
}template<class UserClass>
bool IDDOO::InvokeRepeat(FName InvokeName, float DelayTime, float RepeatTime, UserClass* UserObj, typename FDDInvokeEvent::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod)
{DDInvokeTask* InvokeTask = new DDInvokeTask(DelayTime, true, RepeatTime);InvokeTask->InvokeEvent.BindUObject(UserObj, InMethod);return IModule->StartInvoke(GetObjectName(), InvokeName, InvokeTask);
}

DDOO.cpp

bool IDDOO::StopInvoke(FName InvokeName)
{return IModule->StopInvoke(GetObjectName(), InvokeName);
}void IDDOO::StopAllInvoke()
{IModule->StopAllInvoke(GetObjectName());
}

最后在 CoroActor.cpp 里调用循环延时方法进行测试。

CoroActor.cpp

void ACoroActor::DDEnable()
{// 测试完后记得注释掉InvokeRepeat("EchoInfo", 3.f, 2.f, this, &ACoroActor::EchoCoroInfo);// TempStartCoroutine(CoroTestTwo());//DDH::Debug() << "StartCoroutine --> " << StartCoroutine("CoroFunc", CoroFunc()) << DDH::Endl();
}

编译后运行,3 秒输出第一句,随后每 2 秒输出一次。

延时系统

38. 协程逻辑优化更新

之前写的协程系统还有一点 Bug 需要解决,我们先复现一下问题:

CoroActor.h

protected:DDCoroTask* CoroFixed();	// 使用协程的方法void StopCoro();	// 负责调用停止协程的方法

CoroActor.cpp

void ACoroActor::DDEnable()
{// 本节课结束后记得注释掉StartCoroutine("CoroFixed", CoroFixed());
}DDCoroTask* ACoroActor::CoroFixed()
{DDCORO_PARAM(ACoroActor);#include DDCORO_BEGIN()#include DDYIELD_READY()DDYIELD_RETURN_SECOND(5.f);		// 挂起 5 秒DDH::Debug() << "StopCoro" << DDH::Endl();D->StopCoro();		// 在第一次挂起时停止协程#include DDYIELD_READY()DDYIELD_RETURN_SECOND(3.f);		// 挂起 3 秒DDH::Debug() << "StopCoroComplete" << DDH::Endl();#include DDCORO_END()
}void ACoroActor::StopCoro()
{StopCoroutine("CoroFixed");
}

编译后运行,项目应该在输出那一瞬间就崩溃了。崩溃的原因是:DDMessage.cpp 的逻辑里,Work() 方法调用了停止协程方法 StopCoroutine(),将协程任务移出了容器;但是后续调用 IsFinish() 判断时依旧会访问这个协程任务原来在容器里的位置,导致访问到错误地址。

所以我们在协程任务结构体里面再添加一个 bool 值,保存协程任务实例是否被删除。这样在 StopCoroutine() 里就不再去进行 “将协程任务移除出容器” 的操作,而是直接更改这个 bool 值;实际的移除操作由 Tick() 全权负责。

DDTypes.h

struct DDCoroTask
{// 是否销毁(老师拼写错了)bool IsDestroy;DDCoroTask(int32 CoroCount){IsDestroy = false;}}

DDMessage.cpp

void UDDMessage::MessageTick(float DeltaSeconds)
{// ... 省略if (Ih->Value->IsFinish() || Ih->Value->IsDestroy) {	// 添加多一个判断delete Ih->Value;CompleteNode.Push(Ih->Key);}// ... 省略
}bool UDDMessage::StopCoroutine(FName ObjectName, FName CoroName)
{if (CoroStack.Contains(ObjectName) && CoroStack.Find(ObjectName)->Find(CoroName)) {// 修改如下(*(CoroStack.Find(ObjectName)->Find(CoroName)))->IsDestroy = true;return true;}return false;
}void UDDMessage::StopAllCoroutine(FName ObjectName)
{if (CoroStack.Contains(ObjectName)) {for (TMap<FName, DDCoroTask*>::TIterator It(*CoroStack.Find(ObjectName)); It; ++It)// 修改如下It->Value->IsDestroy = true;}
}

编译后运行,打印一条 “StopCoro” 语句后就不会打印下一条,游戏也没有崩溃,说明修改成功了。

依葫芦画瓢地改一下延时系统,因为延时系统是基本照搬协程系统的。

DDTypes.h

struct DDInvokeTask
{// 是否销毁bool IsDestroy;DDInvokeTask(float InDelayTime, bool InIsRepeat, float InRepeatTime){IsDestroy = false;}}

DDMessage.cpp

void UDDMessage::MessageTick(float DeltaSeconds)
{// ... 省略if (Ih->Value->UpdateOperate(DeltaSeconds) || Ih->Value->IsDestroy) {	// 添加多一个判断delete Ih->Value;CompleteNode.Push(Ih->Key);}// ... 省略
}bool UDDMessage::StopInvoke(FName ObjectName, FName InvokeName)
{if (InvokeStack.Contains(ObjectName) && InvokeStack.Find(ObjectName)->Find(InvokeName)) {(*(InvokeStack.Find(ObjectName)->Find(InvokeName)))->IsDestroy = true;return true;}return false;
}void UDDMessage::StopAllInvoke(FName ObjectName)
{if (InvokeStack.Contains(ObjectName)) {for (TMap<FName, DDInvokeTask*>::TIterator It(*InvokeStack.Find(ObjectName)); It; ++It)It->Value->IsDestroy = true;}
}

39. 普通按键绑定

下面这段文字截取自梁迪老师准备的 DataDriven 文档:

UE4的按键绑定需要调用 ACharactor 下的 SetupPlayerInputComponent(),或者 APlayerController 下的 SetupInputComponent() 进行按键事件的绑定,如果要实现 “按下 Esc 键弹出菜单” 的功能,就需要获取 UI 对象的指针与添加头文件来进行绑定,这样的话耦合程度较高。因此 DataDriven 框架提供一套自己的按键绑定系统,可以在任何对象下进行按键事件的绑定,并且提供多按键事件绑定功能。

按键绑定系统的功能包括:绑定 Axis 按键、触摸按键、单个按键和多个按键(同时按下)。

来到 DDMessage,它首先要获得玩家控制器,通过 UDDCommon 就可以获取。这样就可以通过玩家控制器访问绑定按钮的函数。

四个模板方法对应上面列出的 4 种绑定按键的类型。

DDMessage.h

#include "GameFramework/PlayerController.h"	// 引入头文件
#include "DDMessage.generated.h"UCLASS()
class DATADRIVEN_API UDDMessage : public UObject, public IDDMM
{GENERATED_BODY()public:// 绑定 Axis 按键事件template<class UserClass>FInputAxisBinding& BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName);// 绑定触摸事件template<class UserClass>FInputTouchBinding& BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent);// 绑定 Action 按键事件template<class UserClass>FInputActionBinding& BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent);// 绑定单个按键事件template<class UserClass>FInputKeyBinding& BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent);protected:// PlayerController 指针APlayerController* PlayerController;
};template<class UserClass>
FInputAxisBinding& UDDMessage::BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName)
{return PlayerController->InputComponent->BindAxis(AxisName, UserObj, InMethod);
}template<class UserClass>
FInputTouchBinding& UDDMessage::BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent)
{return PlayerController->InputComponent->BindTouch(KeyEvent, UserObj, InMethod);
}template<class UserClass>FInputActionBinding& UDDMessage::BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent)
{return PlayerController->InputComponent->BindAction(ActionName, KeyEvent, UserObj, InMethod);
}template<class UserClass>
FInputKeyBinding& UDDMessage::BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent)
{return PlayerController->InputComponent->BindKey(Key, KeyEvent, UserObj, InMethod);
}

DDMessage.cpp

void UDDMessage::MessageBeginPlay()
{// 从 UDDCommon 获取 ControllerPlayerController = UDDCommon::Get()->GetController();
}

依旧是建立 DDMessage – DDModule – DDOO – 对象 的调用链。

DDModule.h

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DATADRIVEN_API UDDModule : public USceneComponent
{GENERATED_BODY()public:// 绑定 Axis 按键事件template<class UserClass>FInputAxisBinding& BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName);// 绑定触摸事件template<class UserClass>FInputTouchBinding& BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent);// 绑定 Action 按键事件template<class UserClass>FInputActionBinding& BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent);// 绑定单个按键事件template<class UserClass>FInputKeyBinding& BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent);};template<class UserClass>
FInputAxisBinding& UDDModule::BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName)
{return Message->BindAxis(UserObj, InMethod, AxisName);
}template<class UserClass>
FInputTouchBinding& UDDModule::BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent)
{return Message->BindTouch(UserObj, InMethod, KeyEvent);
}template<class UserClass>
FInputActionBinding& UDDModule::BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent)
{return Message->BindAction(UserObj, InMethod, ActionName, KeyEvent);
}template<class UserClass>
FInputKeyBinding& UDDModule::BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent)
{return Message->BindInput(UserObj, InMethod, Key, KeyEvent);
}

DDOO.h

class DATADRIVEN_API IDDOO
{GENERATED_BODY()protected:// 绑定 Axis 按键事件template<class UserClass>FInputAxisBinding& BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName);// 绑定触摸事件template<class UserClass>FInputTouchBinding& BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent);// 绑定 Action 按键事件template<class UserClass>FInputActionBinding& BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent);// 绑定单个按键事件template<class UserClass>FInputKeyBinding& BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent);
};template<class UserClass>
FInputAxisBinding& IDDOO::BindAxis(UserClass* UserObj, typename FInputAxisHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName AxisName)
{return IModule->BindAxis(UserObj, InMethod, AxisName);
}template<class UserClass>
FInputTouchBinding& IDDOO::BindTouch(UserClass* UserObj, typename FInputTouchHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const EInputEvent KeyEvent)
{return IModule->BindTouch(UserObj, InMethod, KeyEvent);
}template<class UserClass>
FInputActionBinding& IDDOO::BindAction(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FName ActionName, const EInputEvent KeyEvent)
{return IModule->BindAction(UserObj, InMethod, ActionName, KeyEvent);
}template<class UserClass>
FInputKeyBinding& IDDOO::BindInput(UserClass* UserObj, typename FInputActionHandlerSignature::TUObjectMethodDelegate<UserClass>::FMethodPtr InMethod, const FKey Key, const EInputEvent KeyEvent)
{return IModule->BindInput(UserObj, InMethod, Key, KeyEvent);
}

最后来简单测试一下绑定单个按键事件的方法。

CoroActor.h

protected:void BKeyEvent();

CoroActor.cpp

void ACoroActor::DDEnable()
{BindInput(this, &ACoroActor::BKeyEvent, EKeys::B, IE_Pressed);
}void ACoroActor::BKeyEvent()
{DDH::Debug() << "BKeyEvent" << DDH::Endl();
}

来到项目的 .Build.cs 文件,需要添加对 Slate 的依赖。

RaceCarFrame.Build.cs

		// 需要添加对 Slate 的依赖,否则会报错PrivateDependencyModuleNames.AddRange(new string[] {"Slate","SlateCore",});PublicDefinitions.Add("HMD_MODULE_INCLUDED=1");

编译后运行,此时按一次 B 键可以让左上角输出一次 “BKeyEvent”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/226608.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BUG-由浏览器缩放引起PC端显示手机端视图

文章目录 来源解决 来源 启动Vue项目&#xff0c;用浏览器打开显示手机端视图&#xff0c;从vscode直接ctrl链接打开正常显示。 检查-未开启仿真&#xff0c;但仍显示错误。 解决 浏览器缩放问题。 修改为100%

ArcGIS高程点生成等高线

基本步骤&#xff1a;数据清洗→创建TIN→TIN转栅格→等值线→平滑线。 1.&#xff08;重要&#xff09;数据清理&#xff1a;删除高程点中的高程异常值数据。 2.创建TIN:系统工具→3D Analyst Tools→数据管理→TIN→创建TIN&#xff08;可直接搜索工具TIN&#xff09;。 单击…

十二:爬虫-Scrapy框架(上)

一&#xff1a;Scrapy介绍 1.Scrapy是什么&#xff1f; Scrapy 是用 Python 实现的一个为了爬取网站数据、提取结构性数据而编写的应用框架(异步爬虫框架) 通常我们可以很简单的通过 Scrapy 框架实现一个爬虫&#xff0c;抓取指定网站的内容或图片 Scrapy使用了Twisted异步网…

LabVIEW的便携式车辆振动测试分析

随着计算机和软件技术的发展&#xff0c;虚拟仪器正逐渐成为机械工业测试领域的主流。在现代机械工程中&#xff0c;特别是车辆振动测试&#xff0c;传统的测试方法不仅设备繁杂、成本高昂&#xff0c;而且操作复杂。为解决这些问题&#xff0c;开发了一款基于美国国家仪器公司…

1. pytorch mnist 手写数字识别

文章目录 一、数据集介绍1.1、简介1.2 详细介绍1、数据量2、标注量3. 标注类别4.数据下载5.数据集解读 二、读取、加载数据集1、pytorch 自带库函数2、通过重构Dataset类读取特定的MNIST数据或者制作自己的MNIST数据集 三、模型构建四、 runtraintest评估模型的性能检查点的持续…

在Java中使用sort()方法进行排序

思想 采用Array类的sort()方法&#xff0c;sort()可以对数组进行排序,有很多重载格式&#xff0c;可以对任何数据类型进行不同数据类型的排序。 代码 import java.util.Arrays; import java.util.Random; public class SortSequence {public static void main(String[] arg…

C# ASP.NET 实验室 检验中心 医疗LIS源码

LIS系统能够自动处理大量的医学数据&#xff0c;包括样本采集、样本处理、检测分析、报告生成等。它能够快速、准确地进行化验检测&#xff0c;提高医院的运营效率。LIS系统还提供了丰富的数据分析功能&#xff0c;能够对医院化验室的业务流程进行全面、细致的监控。 LIS系统优…

【SpringBoot篇】详解Bean的管理(获取bean,bean的作用域,第三方bean)

文章目录 &#x1f354;Bean的获取&#x1f384;注入IOC容器对象⭐代码实现&#x1f6f8;根据bean的名称获取&#x1f6f8;根据bean的类型获取&#x1f6f8;根据bean的名称和类型获取 &#x1f384;Bean的作用域⭐代码实现&#x1f388;注意 &#x1f384;第三方Bean⭐代码实现…

iPortal内置Elasticsearch启动失败的几种情况——Linux

作者&#xff1a;yx 文章目录 前言一、端口占用二、ES启动过慢三、磁盘占用过高&#xff0c;导致ES变为只读模式 前言 在Linux环境启动iPortal后有时会出现搜索异常的情况&#xff0c;如下截图&#xff0c;这是因为Elasticsearch&#xff08;以下简称“ES”&#xff09;没启动…

【Midjourney】Midjourney根据prompt提示词生成人物图片

目录 &#x1f347;&#x1f347;Midjourney是什么&#xff1f; &#x1f349;&#x1f349;Midjourney怎么用&#xff1f; &#x1f514;&#x1f514;Midjourney提示词格式 Midjourney生成任务示例 例1——航空客舱与乘客 prompt prompt翻译 生成效果 大图展示 细节大…

K8s实战-init容器

概念&#xff1a; 初始化容器的概念 比如一个容器A依赖其他容器&#xff0c;可以为A设置多个 依赖容易A1&#xff0c;A2&#xff0c;A3 A1,A2,A3要按照顺序启动&#xff0c;A1没有启动启动起来的 话&#xff0c;A2,A3是不会启动的&#xff0c;直到所有的静态容器全 部启动完毕…

Python数据分析之Pandas的数据加载与预处理

一、数据分析 数据分析就是让看似杂乱无章的数据产生价值&#xff0c;通过数据的筛选、汇总等等操作将数据背后的信息集中和提炼出来&#xff0c;最终分析出一个结果或预测出事件的变化规律。 二、数据清洗 数据加载与预览 1、去除/填充有缺失的数据 2、去除/修改格式错误或…

ChatGPT4.0(中文版)国内无限制免费版(附网址)

ChatGPT&#xff0c;由OpenAI开发的人工智能语言模型。它是你的数字对话伙伴&#xff0c;无论你有何问题或需要什么帮助&#xff0c;它都能提供有用的信息。 经过不断的研发和更新&#xff0c;ChatGPT的性能和功能得到了显著提升。现在&#xff0c;我们将重点介绍ChatGPT的两个…

C#高级 08Json操作

1.概念 Json是存储和交换文本信息的语法。类似于XML。Json比XML更小、更快、更易解析。Json与XML一样是一种数据格式。Json是一种轻量级的数据交换格式。它基于ECMAScript的一个子集。Json采取完全独立于语言的文本格式&#xff0c; 但是也使用了类似于C语言的习惯。这些特性使…

创建VLAN及VLAN间通信

任务1、任务2、任务3实验背景&#xff1a; 在一家微型企业中&#xff0c;企业的办公区域分为两个房间&#xff0c;一个小房间为老板办公室&#xff0c;一个大房间为开放办公室&#xff0c;财务部和销售部的员工共同使用这个办公空间。我们需要通过VLAN的划分&#xff0c;使老板…

3D视觉-激光三角测量法的分类

按照入射激光光束和被测物体表面法线的角度关系&#xff0c;一般分为直射式和斜射式两种方式。 1&#xff09;直射式测量 如图所示&#xff0c;激光器发出的光线&#xff0c;经会聚透镜聚焦后垂直入射到被测物体表面上&#xff0c;物体移动或者其表面变化&#xff0c;导致入射…

elasticsearch系列四:集群常规运维

概述 在使用es中如果遇到了集群不可写入或者部分索引状态unassigned&#xff0c;明明写入了很多数据但是查不到等等系列问题该怎么办呢&#xff1f;咱们今天一起看下常用运维命令。 案例 起初我们es性能还跟得上&#xff0c;随着业务发展壮大&#xff0c;发现查询性能越来越不…

鸿蒙开发(二)- 鸿蒙DevEco3.X开发环境搭建

上篇说到&#xff0c;鸿蒙开发目前势头旺盛&#xff0c;头部大厂正在如火如荼地进行着&#xff0c;华为也对外宣称已经跟多个厂商达成合作。目前看来&#xff0c;对于前端或客户端开发人员来说&#xff0c;掌握下鸿蒙开发还是有些必要性的。如果你之前是从事Android开发的&…

Jackson ImmunoResearch纳米二抗(Nano Secondary Antibodies)

驼科&#xff0c;如羊驼和美洲驼&#xff0c;会产生一类独特的仅由重链组成的抗体。而抗原结合片段(Fab)&#xff0c;也称为仅可变重链片段抗体(Variable Heavy-Chain only fragment antibodies&#xff0c;VHH片段)&#xff0c;或纳米抗体&#xff0c;是一种新型抗体形式。凭借…

GBASE南大通用数据库提供的高可用负载均衡功能

GBASE南大通用GBase 8a ODBC 提供的高可用负载均衡功能是指&#xff0c;GBase 8a ODBC 会将客户 端请求的数据库集群连接平均分摊到集群所有可用的节点上。 GBASE南大通用数据库负载均衡的使用方法 GBASE南大通用GBase 8a ODBC 提供两种方式来使用高可用负载均衡。一种是配置数…