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.mzph.cn/news/585007.shtml

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

相关文章

AIGC开发:调用openai的API接口实现简单机器人

简介 开始进行最简单的使用&#xff1a;通过API调用openai的模型能力 OpenAI的能力如下图&#xff1a; 文本生成模型 OpenAI 的文本生成模型&#xff08;通常称为生成式预训练 Transformer 或大型语言模型&#xff09;经过训练可以理解自然语言、代码和图像。这些模型提供文…

RabbitMQ消息存储JSON格式反序列化

如果发送消息消息体为实体类对象数据&#xff0c;交换机接收消息经由路由键发送给队列。需要实现数据反序列化操作。实现JSON格式的反序列化操作 Rabbitmq的反序列化接口 MessageConverter&#xff0c;它的实现类有 Jackson2JsonMessageConverter的反序列化实现类&#xff0c…

数据仓库系列01-规划篇

企业在构建数据仓库时&#xff0c;首先需要整体上对数据仓库进行规划&#xff0c;制定规范。数仓架构师需要对数仓分层、业务分类、数据域、业务过程、数据集市、主题域进行设计。这样模型设计时&#xff0c;可以将模型关联到数仓分层、业务分类、数据域、业务过程等对所建模型…

openssl的 openssl.cnf配置文件详解

背景&#xff1a;在上一篇文中&#xff0c;提到要写一篇openssl 配置文件详解的&#xff0c;这就来了~~~ find / -name openssl.cnf /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf&#xff0c;该文件主要设置了证书请求、签名、crl相关的配置。主要相关的伪命令为ca和req…

HarmonyOS page生命周期函数讲解

下面 我们又要看一个比较重要的点了 页面生命周期 页面组件有三个生命周期 onPageShow 页面显示时触发 onPageHide 页面隐藏时触发 onBackPress 页面返回时触发 这里 我们准备两个组件 首先是 index.ets 参考代码如下 import router from ohos.router Entry Component struc…

js+css实现颜色选择器

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>颜色选择器</title><style>.color-box {width: 50px;height: 50px;border: 1px solid #000;cursor: pointer;}</style> </head> <body><…

小米电脑管家 - 手机平板电脑家居互联

系列文章目录 前言 联想电脑安装小米电脑管家实现设备互联 如图&#xff0c;将 小米平板 5 Pro 作为联想笔记本 GeekPro 5000 &#xff08;这垃圾电脑&#xff09;的副屏。 可以在小米平板控制笔记本&#xff0c;如图所示 一、官方使用手册 参考&#xff1a;小米电脑管家帮助 …

AtCoder ABC186

E题和F题两个绿题。 C - Unlucky 7 单纯的模拟一下。 # -*- coding: utf-8 -*- # time : 2023/6/2 13:30 # file : atcoder.py # software : PyCharmimport bisect import copy import sys from itertools import permutations from sortedcontainers import Sorted…

最新GPT4教程,GPT语音对话使用,Midjourney绘画,ChatFile文档对话总结+DALL-E3文生图教程工具

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

【前端面经】即时设计

目录 前言一面git 常见命令跨窗口通信vue 响应式原理发布订阅模式翻转二叉树Promise.all()扁平化数组面试官建议 二面Event Loop 原理Promise 相关css 描边方式requestAnimationReact 18 新特性JSX 相关react 输出两次函数式编程React 批处理机制http请求头有哪些本地存储性能优…

Go语言中关于go get, go install, go build, go run指令

go get go get 它会执行两个操作 第一个, 是先将远程的代码克隆到Go Path的 src 目录那二个, 是执行go install命令 那如果指定的包可以生成二进制文件那它就会把这个二进制文件保存到这个 Go Path 的bin目录下面这是 go install 命令执行的操作 如果只需要下载包&#xff0c…

Java核心技术卷接口的实现与继承多态知识梳理总结

Java核心技术卷接口的实现与继承多态知识梳理总结 接口的概念 在Java程序设计语言中&#xff0c;接口不是类&#xff0c;而是对希望符合这个接口的类的一组需求。 form&#xff1a; Java核心技术卷 I&#xff08;原书第11版&#xff09; 基础知识 by 凯 S.霍斯特曼 在Java中&a…

Spring Boot整合GraphQL

RPC选型入门测试系列文章 GraphQL是一种用于API开发的查询语言和运行时环境。它由Facebook开发并于2015年开源。GraphQL的主要目标是提供一种更高效、灵活和易于使用的方式来获取和操作数据。与传统的RESTful API相比&#xff0c;GraphQL允许客户端精确地指定需要的数据&#…

Unity中Shader裁剪空间推导(在Shader中使用)

文章目录 前言一、在Shader中使用转化矩阵1、在顶点着色器中定义转化矩阵2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵3、定义一个枚举用于区分当前是处于什么相机 二、我们在DirectX平台下&#xff0c;看看效果1、正交相机下2、透视相机下3、最终代码 前言 在上一篇文章中&…

高光回眸:阿里云容器服务如何全面助力精彩亚运

作者&#xff1a;刘佳旭 谢乘胜 贤维 引言 2023 年&#xff0c;第 19 届杭州亚运会在杭州成功举办。在亚运之光和科技之光的交相辉映下&#xff0c;这届亚运会成为亚运史上首届“云上亚运”&#xff0c;用云计算创造了历史&#xff0c;赛事核心系统和转播全面上云&#xff0c…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus 1. 根的作用2. 手绘技巧3. 分离点/汇合点&根轨迹的几何性质 1. 根的作用 G ( s ) s 3 s 2 2 s 4 G\left( s \right) \frac{s3}{s^22s4} G(s)s22s4s3​…

5-Docker实例-安装mysql

1.拉取mysql镜像 命令: docker search mysql docker pull mysql:5.6 [root@centos79 run]# docker pull mysql:5.6 5.6: Pulling from library/mysql 35b2232c987e: Pull complete fc55c00e48f2: Pull complete 0030405130e3: Pull complete e1fef7f6a8d1: Pull comple…

Qt Quick 用cmake怎么玩子项目

以下内容为本人的著作&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/o-_aGqreuQda-ZmKktvxwA 以往在公司开发众多的项目中&#xff0c;都会出现要求本项目里部分功能模块代码需要具备保密性。如果需要对外输出demo工程&…

图像识别的技术前沿:人工智能与机器学习的融合

图像识别的技术前沿在于人工智能&#xff08;AI&#xff09;与机器学习&#xff08;ML&#xff09;的融合。这种融合使得图像识别系统能够从大量数据中自动学习并识别出各种模式&#xff0c;从而在复杂和动态的环境中实现更高的准确性和鲁棒性。 机器学习在图像识别中发挥着越…

Thinkphp 5框架学习

TP框架主要是通过URL实现操作 http://servername/index.php/模块/控制器/操作/参数/值.. index.php 为入口文件&#xff0c;在 public 目录内的 index.php 文件; 模块在 application 目录下默认有一个 index 目录&#xff0c;这就是一个模块; 而在 index 目录下有一个 contro…