UE 简单插件制作

news2024/11/25 1:37:41

本文主要是提供几个写UE插件的实例,借此来了解在UE里使用C++创建自定义插件的做法:

  • 写一个使场景变暗的简单插件
  • 写一个自定义窗口,展示项目里所有的动画资产

写一个使场景变暗的简单插件

参考:Unreal Engine 5 - Writing Plugins in C++

写一个简单的插件,来了解在UE里使用C++创建自定义插件的做法,这里会给Viewport窗口添加一个Button,点击按钮后会在场景里添加一个PoseProcessVolume类型的对象,使用它将场景变暗,实现Night效果,如下图所示:
在这里插入图片描述

这里还附带一些参数操作:

  • 创建的PoseProcessVolumn的PoseProcessVolume Settings里勾选Infinete Extent,让这个后处理范围是无限的
  • DirectionalLight的intensity改变为1.0
  • PostProcessVolumn,调整其Expoture->ExposureCompensation值为-3

创建Editor Toolbar Button类型的Plugin

在我的C++工程类型的UE项目里,从菜单中选择"Edit"->“Plugins”,点出“New Plgin”,由于要修改的是EditorToolbar,所以在弹出的窗口中选中“Editor Toolbar Button”,创建一个名为“MakeLevelDark”的plugin (PS: 蓝图类型的UE项目貌似是没有这么多选项)。

这里我创建失败了,可能是因为我的引擎版本升级了,原来的项目没升级,导致项目有编译报错Unable to clean target while not hot-reloading.close the editor and try again,所以我把原本项目的Binaries、Intermediate、Saved和*.sln文件删掉了,重新导入了一次。才创建成功:
在这里插入图片描述

会自动打开VS2022,可以看到新创建的相关文件,MakeLevelDark、MakeLevelDarkCommands和MakeLevelDarkStyle三个类,以及一个Module的Build.cs文件:
在这里插入图片描述

而且此时的Viewport的Toolbar就会自动出现对应的Button了:
在这里插入图片描述
点击之后会提示:
在这里插入图片描述

所以这里需要Override对应Module里的PluginButtonClicked函数,来执行自定义的内容,对应的函数所在位置对应的Module类:

#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class FToolBarBuilder;
class FMenuBuilder;

class FMakeLevelDarkModule : public IModuleInterface
{
public:

	/** IModuleInterface implementation */
	virtual void StartupModule() override;
	virtual void ShutdownModule() override;
	
	// 需要自定义的函数
	void PluginButtonClicked();
	
private:

	void RegisterMenus();


private:
	TSharedPtr<class FUICommandList> PluginCommands;
};

写C++代码

基于具体功能,分为以下几个部分:

  • 点击按钮时,找到Level里的Directional Light,改变其Intensity
  • 判断场景里是否存在PostProcessVolume,如果不存在,则给一个Message提示,并创建一个新的
  • 修改PoseProcessVolume的Extent和Exposure Compensation参数

Directional Light部分
为了找到Level里的Directional Light,这里设计一个通用的在场景里找Actor的函数:

#include <Kismet/GameplayStatics.h>

// 返回场景里找到的特定类型的第一个对象
AActor* FMakeLevelDarkModule::FindActor(TSubclassOf<AActor> ActorClass)
{
    // 获取World
	const UWorld* World = GEditor->GetEditorWorldContext().World();
	if (World)
	{
		TArray<AActor*>  FoundActors;
		UGameplayStatics::GetAllActorsOfClass(World, ActorClass, FoundActors);
		if (FoundActors.Num() > 0)
			return FoundActors[0];
	}

	return nullptr;
}

然后利用此函数,找到Level里的Directional Light,改变其Intensity:

AActor* FoundActor = FindActor(ADirectionalLight::StaticClass());

if (FoundActor)
{
	ADirectionalLight* Sun = Cast<ADirectionalLight>(FoundActor);

	if (Sun)
		Sun->GetLightComponent()->SetIntensity(1.f);
}

PS: VS 2022在Components文件夹下的Include路径提示没了,老给我报错,然后编译其实是好的,坑了我半天。。。


PostProcessVolume部分
这里可能需要在场景里添加PostProcessVolume对应的Actor,所以也设计个函数:

AActor* FMakeLevelDarkModule::AddActor(TSubclassOf<AActor> ActorClass)
{
	ULevel* Level = GEditor->GetEditorWorldContext().World()->GetCurrentLevel();
	return GEditor->AddActor(Level, ActorClass, FTransform());
}

然后继续后面的逻辑:

FoundActor = FindActor(APostProcessVolume::StaticClass());

// 没找到则创建
if (!FoundActor)
{
	DialogText = FText::FromString("PostProcessVolume Not Found, Creating One");
    // 在创建PoseProcessVolume的时候加个Message提示
	FMessageDialog::Open(EAppMsgType::Ok, DialogText);

	FoundActor = AddActor(APostProcessVolume::StaticClass());
}

// 直接修改
if (FoundActor)
{
	APostProcessVolume* PPVol;
	PPVol = Cast<APostProcessVolume>(FoundActor);
	if (PPVol)
	{
		PPVol->Settings.AutoExposureBias = -3.f;
		PPVol->Settings.bOverride_AutoExposureBias = true;
		PPVol->bUnbound = true;
	}
}


自定义窗口,展示项目里所有的动画资产

貌似是有两种做法:

  • 通过蓝图实现,创建Editor Widget蓝图(注意是Editor Widget资产,不是Widget Blueprint)
  • 通过C++创建Plugins实现

参考:Slate Editor Window Quickstart Guide

第一种方式就不提了,它也不是个插件,这里研究的是使用C++创建Slate Editor Window来创建插件

前面没写过Slate相关的内容,这里先学习一下,具体分为以下几步:

  • 创建插件
  • 写一个简单的Widget类,加入到插件对应的Editor Window里,学习Slate C++的写法
  • 把这个Widget类变得复杂,让它可以绘制出所有动画资产

创建Editor Standalone Window类型的插件

这个就不多说了,我创建的插件名为AnimationLibraryPreviewer,结果这次又报错了:

Failed to compile plugin source code. See output log for more information.

跟Live Coding好像也没关系,很烦,只能又删除Cache文件重新导入了。。。。。

此时会创建以下文件,可以发现跟前面的Editor Toolbar Button模板插件创建的文件相同:
在这里插入图片描述

基于UE的模板,此时就已经可以在Window的下拉页面里找到打开Editor Window的Menu了,初始的窗口如下图所示:
在这里插入图片描述

窗口上让我去改FAnimationPreviewLibraryModule::OnSpawnPluginTab函数,可以来看看对应的代码,来学习下UE里描述Slate的语法:

TSharedRef<SDockTab> FAnimationPreviewLibraryModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
	// 创建一个长Text
    FText WidgetText = FText::Format(
        LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"),
        FText::FromString(TEXT("FAnimationPreviewLibraryModule::OnSpawnPluginTab")),
        FText::FromString(TEXT("FAnimationPreviewLibraryModule.cpp"))
        );

    return SNew(SDockTab)					// 1. 创建了DockableTab
    .TabRole(ETabRole::NomadTab)			// 2. 指定Tab类型
    [
        // Put your tab content here!
        SNew(SBox)							// 3. 创建Box
        .HAlign(HAlign_Center)				// 4. 水平和竖直方向的align方式都为居中
        .VAlign(VAlign_Center)
        [
            SNew(STextBlock)				// 5. 里面再创建一个TextBlock, 指定Text为WidgetText
            .Text(WidgetText)
        ]
    ];

}

重点如下:

  • SNew(SDockTab)创建了一个SDockTab,代表着这个Dockable的EditorWindow
  • ETabRole有五种类型:MajorTab, PanelTab, NomadTab, DocumentTab, NumRoles, 这里的Nomad是游牧民族的意思,这里可以翻译为游离的Tab,挺形象
  • 这里面UI Element的父子关系是通过[]实现的

其实用UMG创建Widget蓝图,也差不多是这么个操作,UMG是基于Slate封装(Slate的UObject性质的一层封装)的可视化编辑器,它还添加了很多事件和方法,更多的Slate语法参考Slate Overview

创建Menu类

You can add Slate widgets directly to the FAnimationPreviewLibraryModule class generated by the Plugin wizard. However, there are some limitations to how you can handle your widgets’ callbacks if you do this. Therefore, you should create a dedicated Slate widget to hold the menu’s contents, then add that widget to the FAnimationPreviewLibraryModule class.

这里并没有选择直接在FAnimationPreviewLibraryModule类里添加Slate widgets,而是选择在对应的插件工程里创建了一个新的Slate WidgetAnimPreviewLibraryWindowMenu,其实就是创建了一个自定义的Slate Widget(类似于Unity里的自定义Visual Element)。

如下图所示,注意要在下拉箭头选择对应的Project,不然就会加到默认的Game Code里
在这里插入图片描述

创建的类会自带一个Construct函数:

// SAnimPreviewLibraryWindowMenu.cpp
#include "SAnimPreviewLibraryWindowMenu.h"
#include "SlateOptMacros.h"

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SAnimPreviewLibraryWindowMenu::Construct(const FArguments& InArgs)
{
	// 注意这里的[]是必须要有内容的, 否则会编译报错
	/*
	ChildSlot
	[
		// Populate the widget
	];
	*/
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

这里填充代码为:

void SAnimPreviewLibraryWindowMenu::Construct(const FArguments& InArgs)
{
    ChildSlot
    [
        SNew(SVerticalBox)                                      // 1. 先创建SVerticalBox
        + SVerticalBox::Slot()                                  // 2. 添加Slot, 里面存SHorizontalBox, 代表第一行
        .AutoHeight()
        [
            SNew(SHorizontalBox)
            + SHorizontalBox::Slot()
            .VAlign(VAlign_Top)
            [
                SNew(STextBlock)
                .Text(FText::FromString("Test Button"))
            ]
            + SHorizontalBox::Slot()
            .VAlign(VAlign_Top)
            [
                SNew(SButton)
                .Text(FText::FromString("Press Me"))
            ]
        ]
        + SVerticalBox::Slot()                                  // 3. 添加Slot, 里面存SHorizontalBox, 代表第二行
        .AutoHeight()
        [
            SNew(SHorizontalBox)
            + SHorizontalBox::Slot()
            .VAlign(VAlign_Top)
            [
                SNew(STextBlock)
                .Text(FText::FromString("Test Checkbox"))
            ]
            + SHorizontalBox::Slot()
            .VAlign(VAlign_Top)
            [
                SNew(SCheckBox)
            ]
        ]
     ];
}

接下来,需要让原本的Editor窗口创建刚刚创建的UI Element,需要修改原本的OnSpawnPluginTab,代码如下:

TSharedRef<SDockTab> FAnimationPreviewLibraryModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
	return SNew(SDockTab)
		.TabRole(ETabRole::NomadTab)
		[
			// Put your tab content here!
			SNew(SAnimPreviewLibraryWindowMenu)
		];
}

You can’t compile Slate windows while Live Coding is active and your project is open in Unreal Editor. Make sure to close Unreal Editor before trying to compile your code.

关闭Editor,在VS里按Ctrl + F5,重新启动,再打开编辑器窗口即可,如下图所示:
在这里插入图片描述

简单提一下这里相关Event的处理方法,思路就是在创建Button类似的Widget时,为其指定回调函数

比如Button的写法:

+SHorizontalBox::Slot()
.VAlign(VAlign_Top)
[
    SNew(SButton)
    .Text(FText::FromString("Press Me"))
    // SP代表Shared pointer delegates
    // FOnClicked是UE里给Slate提供回调的类, OnTestButtonClicked是此类自定义的函数
    .OnClicked(FOnClicked::CreateSP(this, &SAnimPreviewLibraryWindowMenu::OnTestButtonClicked))
]

// 回调函数签名为:
FReply SAnimPreviewLibraryWindowMenu::OnTestButtonClicked()
{
    return FReply::Handled();
}

Checkbox的回调如下所示:

+SHorizontalBox::Slot()
.VAlign(VAlign_Top)
[
	// 添加对CheckBox的回调
    SNew(SCheckBox)
    // 当CheckState改变时的回调
    .OnCheckStateChanged(FOnCheckStateChanged::CreateSP(this, &SAnimPreviewLibraryWindowMenu::OnTestCheckboxStateChanged))
    // IsChecked应该是Check的时候的回调吧
    .IsChecked(FIsChecked::CreateSP(this, &SAnimPreviewLibraryWindowMenu::IsTestBoxChecked))
]

加载资产

这里的想法是:

  • 找到项目里面Content文件夹下的所有的uasset的路径
  • 把uasset一一加载进来,判断哪些类型为AnimAsset
  • 把这些动画资产绘制到Editor Window上,每个资产对应一个Object Field
  • 点击动画资产时,Project Content窗口自动锁定到对应的文件

加载并找到所有的AnimAsset
代码如下:

TArray<UObject*> MeshAssets;
EngineUtils::FindOrLoadAssetsByPath(TEXT("/Game/"), MeshAssets, EngineUtils::ATL_Regular);

for (UObject* f : MeshAssets)
{
    UAnimationAsset* animx = Cast<UAnimationAsset>(f);
    if (animx)
    {
        UE_LOG(LogTemp, Warning, TEXT("%s"), *FString(animx ->GetName()));
    }
}

动画资产绘制到Editor Window上
本来想绘制的,发现很麻烦,用Widget Reflector查了下UE里绘制类似东西的类,叫SDetailSingleItemRow,这个是带略缩图的,也是绘制在资产的Details里用的Object Field,但是它并没有暴露出来。不过我看到可以用SObjectPropertyEntryBox,虽然它不带略缩图,但是其Construct函数和GetDesiredWidth作为接口暴露出来了。

所以这里直接修改原本的SAnimPreviewLibraryWindowMenu::Construct函数:

void SAnimPreviewLibraryWindowMenu::Construct(const FArguments& InArgs)
{
	// 1. 读资源
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    TArray<FAssetData> AssetData;

    TArray<UObject*> MeshAssets;
    EngineUtils::FindOrLoadAssetsByPath(TEXT("/Game/"), MeshAssets, EngineUtils::ATL_Regular);


    TArray<UAnimationAsset*> AnimAsset;
    for (UObject* f : MeshAssets)
    {
        UAnimationAsset* animx = Cast<UAnimationAsset>(f);
        if (animx)
        {
            UE_LOG(LogTemp, Warning, TEXT("%s"), *FString(animx->GetName()));
            AnimAsset.Add(animx);
        }
    }


	// 2. 绘制一个大的Container
    TSharedPtr<SVerticalBox> Container = SNew(SVerticalBox);
    for (int32 idx = 0; idx < AnimAsset.Num(); idx++)
    {
        Container->AddSlot()
        [
            SNew(SObjectPropertyEntryBox)
            .EnableContentPicker(false)
            .AllowClear(false)
            .AllowedClass(UAnimationAsset::StaticClass())
            .DisplayBrowse(true)
            .DisplayThumbnail(true)
            .ObjectPath(AnimAsset[idx]->GetPathName())
        ];
    }

	// 3. Container加到ChildSlot里
    ChildSlot
    [
        SNew(SVerticalBox)
        + SVerticalBox::Slot()
        .AutoHeight()
        [
            Container.ToSharedRef()
        ]
    ];
}

把一些选项允许用户在Obejct Field里挑选资产的东西禁用掉了,最后效果如下:

在这里插入图片描述

有点丑陋,而且不可以用滚轮,不过都是小问题,就这样吧

顺便提一句,游戏里面可能会创建自定义的资产,然后要打开它,编辑自定义的资产窗口,此时需要借助FAssetEditorToolkit类完成



附录

关于Slots

参考:https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/UMG/UserGuide/Slots/
参考:https://forums.unrealengine.com/t/how-to-define-a-slate-via-c/385892

Slots are the invisible glue that bind Widgets together. In Slate they are a lot more explicit in that you must first create a Slot then choose what control to place inside of it. In UMG, however, you have Panel Widgets that automatically use the right kind of Slot when Child Widgets are added to them.

Slot用于连接Widget(类似于Unity里的umxl文件用于连接Visual Elements),在UMG里,为Widget添加子Widget时,系统会自动创建Slot,再在里面创建对应的子Widget,而Slate里,则就需要自己手动创建Slot了。

You can declere only one widget in single slot, in order to have more then single widget you need a panel widget. If you ever used UMG you should already understand this concept because UMG inherent this limitation. So if you want to widget one after nother verticuly you can use SVerticalBox, you a…

Slot里只允许有一个widget,要想有多个widget,需要创建panel widget,像UE里,纵向创建UI Element的写法时,需要借助一个SVerticalBox,如下图所示:

SNew(SVerticalBox)
	// 在SVerticalBox的SNew后面连续调用+SVerticalBox::Slot(), 每个Slot里再创建一个子Widget
	+ SVerticalBox::Slot()
	[
        SNew(SButton) 
        Content() 
    	[ 
           SNew(STextBlock) 
        ]
    ]
	+ SVerticalBox::Slot()
	[
        SNew(SButton) 
        Content() 
    	[ 
           SNew(STextBlock) 
        ]
    ]

widget reflector

参考:https://forums.unrealengine.com/t/how-to-define-a-slate-via-c/385892

Now best way to find examples for slate is editor source code, There tool called widget reflector, in Window->Devlopment Tools which let you explore widget tree of editor, so if you wonder how specific widget was done, you scan that widget and you should get name and then you can search it in github to see how this widget got coded.

里面可以看看UE提供的Widget样例,UE4里在Window->Devlopment Tools->widget reflector,UE5里移到了Tools -> Debug->widget reflector里


UE读取文件

参考:https://forums.unrealengine.com/t/using-ffilemanagergeneric-for-listing-files-in-directory/286866/4
参考:https://forums.unrealengine.com/t/find-files-by-extension-ifilemanager-findfilesrecursive/37463/6

主要是因为UE里读取资产的路径不是全局路径,而是它自己弄的这个路径机制,前面其实写过了:

TArray<UObject*> MeshAssets;
EngineUtils::FindOrLoadAssetsByPath(TEXT("/Game/"), MeshAssets, EngineUtils::ATL_Regular);

for (UObject* f : MeshAssets)
{
    UAnimationAsset* animx = Cast<UAnimationAsset>(f);
    if (animx)
    {
        UE_LOG(LogTemp, Warning, TEXT("%s"), *FString(animx ->GetName()));
    }
}

UE获取文件夹下特定的后缀的文件的全局路径

几个重要的点:

  • UE里的路径一般通过FPaths类里的静态成员获取
  • 可以通过FFileManagerGeneric找到特定文件夹下的文件对应的路径

举个例子:

// 找到特定文件夹下的所有xml文件(不包含子文件夹里的文件)
TArray<FString> FileNames;
FFileManagerGeneric FileMgr;
FileMgr.SetSandboxEnabled(true);// don't ask why, I don't know :P
FString wildcard("*.xml"); // May be "" (empty string) to search all files
FString search_path(FPaths::Combine(*FPaths::GameDir(), TEXT("Data"), *wildcard));

FileMgr.FindFiles(FileNames, *search_path, 
                                 true,  // to list files
                                 false); // to skip directories

// 打印出来
for (auto f : FileNames)
{
    FString filename(f);
    UE_LOG(LogTemp, Warning, TEXT("%s"), *f);
}

FileNames.Empty();// Clear array

再举个例子,这个写法是递归的:

TArray<FString> FileNames;
FFileManagerGeneric FileMgr;
FileMgr.SetSandboxEnabled(true);
FString searchFolder(FPaths::Combine(*FPaths::ProjectDir(), TEXT("Content")));

// 其实就是const TCHAR* extension = L"*.uasset";
const TCHAR* extension = _T("*.uasset");
FileMgr.FindFilesRecursive(FileNames, *searchFolder, extension,
    true,  // to list files
    false); // to skip directories

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

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

相关文章

统信UOS专业版系统安装教程 - 手动分区安装UOS系统

全文导读&#xff1a;本文主要介绍了安装UOS系统过程中使用手动分区安装方法&#xff0c;一般没有特殊要求建议使用全盘安装UOS系统。 准备环境 制作好统信UOS专业版启动U盘 一台CPU频率≥2GHz、内存≥4GB、硬盘≥64GB的电脑 安装步骤 一、制作UOS 系统启动盘 制作UOS 系…

自动驾驶TPM技术杂谈 ———— CCRT验收标准(评分标准)

文章目录介绍评价方法指标体系算分方法一级指标二级指标三级指标四级指标五级指标行车辅助能力得分说明跟车能力得分说明前车静止识别与响应得分说明前车低速识别与响应得分说明前车减速识别与响应得分说明前车切入识别与响应得分说明前车切出识别与响应得分说明跟随前车起停得…

数据结构小知识------时间与空间复杂度

本章思维导图&#xff1a; 一&#xff0c;时间复杂度 1.1时间复杂度的概念 &#x1f310;&#xff1a;什么是时间复杂度呢&#xff1f;时间复杂度其实就是一个程序运行时它的指令运行的次数。 在这里&#xff0c;程序默认每条指令的运行时间是一样的。所以时间复杂度就可以理解…

【云原生进阶之容器】第六章容器网络6.4.2--Flannel的安装与部署

1 flannel的安装与部署 见链接一篇文章带你了解Flannel - Flannel - 操作系统 - 深度开源 1.1 部署环境规划 1.2 安装部署 #tar -xf flannel-v0.13.0.tar.gz #mv /apps/svr/flannel-v0.13.0 #ln –svfn /apps/svr/flannel-v0.13.0 /apps/svr/flannel 1.2.1 调整Flannel配置…

设计模式(超详细)

设计模式 原则 什么是SOLID原则&#xff1f; S单一职责SRP Single-Responsibility Principle 一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合,高内聚在面向对象原则的引申,将职责定义为引起变化的原因,以提高内聚性减少引起变化的原因。 比如…

项目资源管理流程:五步专业指南

项目资源管理是描述大多数项目经理的一项关键职能的方式——收集完成工作所需的团队成员、设备和其他材料&#xff08;也称为资源&#xff09;。 以下是项目资源管理的步骤清单&#xff1a; 步骤1&#xff1a;资源规划 为了确定完成项目的资源需求&#xff0c;你首先需要了…

SpringCloud-Gateway实现网关

网关作为流量的入口&#xff0c;常用的功能包括路由转发、权限校验、限流等Spring Cloud 是Spring官方推出的第二代网关框架&#xff0c;由WebFluxNettyReactor实现的响应式的API网关&#xff0c;它不能在传统的servlet容器工作&#xff0c;也不能构建war包。基于Filter的方式提…

个人开发者如何选择阿里云服务器配置CPU内存带宽?

阿里云服务器个人用怎么选择&#xff1f;云服务器吧建议选择ECS共享型s6&#xff0c;不限制CPU性能&#xff0c;选择1核2G或2核4G都可以&#xff0c;云服务器s6处理器采用2.5 GHz主频的Intel Xeon Platinum 8269CY&#xff08;Cascade Lake&#xff09;&#xff0c;睿频3.2 GHz…

【论文阅读--WSOL】Spatial-Aware Token for Weakly Supervised Object Localization

文章目录方法实验Limitation论文&#xff1a;https://arxiv.org/abs/2303.10438代码&#xff1a;https://github.com/wpy1999/SAT/blob/main/Model/SAT.py方法 这篇文章的方法应该属于FAM这一类。 额外添加的一个spatial token&#xff0c;从第10-12层开始&#xff0c;利用其得…

Vue3技术1之Vue3简介、创建Vue3工程、分析工程结构、安装开发者工具与初识setup

Vue3技术1Vue3简介发展提升创建Vue3工程使用vue-cli创建使用vite创建分析工程结构&#xff08;由vue-cli创建的&#xff09;main.jsvue.config.jsApp.vue安装开发者工具初识setupsetup的两种返回值返回一个对象App.vue返回一个函数App.vueVue2与Vue3混合使用App.vue总结Vue3简介…

【致敬未来的攻城狮计划】— 连续打卡第一天:提前对CPK_RA2E1是瑞萨RA系列开发板的初体验,了解一下(文字上的初理解)

系列文章目录 系列文章目录 前言 一、瑞萨MCU&#xff08;CPK_RA2E1是瑞萨RA系列开发板&#xff09;是什么&#xff1f; 首先引入是什么&#xff1f; 他的优势在哪&#xff1f; 瑞萨CPK_RA2E1 对标stm32 相似之处和不同之处&#xff1f; 瑞萨CPK_RA2E1如何开发&#xff…

集成定时器事件

一&#xff0c;定时器事件 1、概述 libevent提供了高性能定时器的功能&#xff0c;方便执行延迟回调逻辑。在添加事件监听的时候&#xff0c;可以不指定fd和监听的事件&#xff0c;指定超时的时间&#xff0c;实现定时器功能。定时器的实现主要依赖下面的数据结构&#xff0c;…

java 多线程基础 万字详解(通俗易懂)

目录 一、前言 二、定义 1.进程 : 2.线程 : 3.单线程与多线程 : 4.并发与并行 : 三、线程的创建 1.创建线程的两种基本方式 : 1 继承Thread类&#xff0c;并重写run方法 1.5 多线程的执行机制(重要) 2 实现Runnable接口&#xff0c;并重写run方法 2. 两种创建线程方式…

【C++】继承---下(子类默认成员函数、虚继承对象模型的详解等)

前言&#xff1a; 上篇文章我们一起初步了解了继承的概念和使用&#xff0c;本章我们回家新一步深入探讨继承更深层次的内容。 前文回顾——>继承---上 目录 &#xff08;一&#xff09;派生类的默认成员函数 &#xff08;1&#xff09;6个默认成员函数 &#xff08;…

Pytorch全连接神经网络实现手写数字识别

问题Mnist手写数字识别数据集作为一个常见数据集&#xff0c;包含10个类别&#xff0c;在此次深度学习的过程中&#xff0c;我们通过pytorch提供的库函数&#xff0c;运用全连接神经网络实现手写数字的识别方法设置参数input_size 784hidden_size 500output_size 10num_epoc…

JavaScript对象类型之function

目录 一、Function 定义函数 调用函数 默认参数 匿名函数 箭头函数 二、函数是对象 三、函数作用域 四、闭包 五、let、var与作用域 一、Function 定义函数 function 函数名(参数) {// 函数体return 结果; } 例如&#xff1a; function add(a, b) {return a b; …

应届生通过Java培训班转行IT有前途吗?

借用邓小平同志曾说过的一句话&#xff1a;科学技术是第一生产力。IT行业作为科技行业中的一员&#xff0c;不管是在自身的发展&#xff0c;还是支持其他行业的发展中都扮演了不可或缺的角色&#xff0c;“互联网”是社会发展的趋势&#xff0c;前途是无限的。而计算机语言是目…

dolphinscheduler之hivecli 任务

hivecli 任务 Hivecli任务说明 dolphinscheduler的hivecli任务是专门执行hivesql的任务类型。其中子类型分为FROM_SCRIPT和FROM_FILE。 FROM_SCRIPT 执行的脚本可以直接在文本框中编写 执行的底层采用-e参数执行 hive -e "show databases;show tables"FROM_FILE…

建造者模式解读

目录 话题引进 传统方式解决盖房需求 传统方式的问题分析 建造者模式基本介绍 基本介绍 四个角色 原理类图 ​编辑 应用实例 改进代码 建造者模式在 JDK 的应用和源码分析 建造者模式的注意事项和细节 抽象工厂模式 VS 建造者模式 话题引进 1) 需要建房子&#xff1a;…

剑指 Offer (第 2 版)

&#xff08;简单&#xff09;剑指 Offer 03. 数组中重复的数字 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请…