综合实践
2D的Shape,Tilemap都要导包的,编辑器也要导包,。。和2d沾边的可能3d都要主动导包
应该综合的去运用,不见得Tilemap就很万能,如果要做什么顶方块的有交互反应的物体,
那直接拖Sprite会更方便一些,不要太局限
同一张图可以用多次,当不同的瓦片资源(设置各自的碰撞器)
为什么 composite collider 2d 组件不允许移除 rigidbody 2d 组件
外包几何由边缘组成,不会保留原始碰撞器的质心或旋转惯性。
CompositeCollider2D附加在动态Rigidbody2D上,因此如果需要,你可能需要明确设置这些
Composite Collider 2D 组件需要 Rigidbody 2D 组件的原因在于它要整合和处理多个碰撞器。以下是详细解释:
-
合并碰撞器:Composite Collider 2D 组件的作用是将多个简单的碰撞器(如 Box Collider 2D 或 Polygon Collider 2D)合并为一个复合碰撞器。这个过程涉及到计算和更新碰撞器的几何形状,以形成一个连续的碰撞形状。
-
动态刚体:当你将多个碰撞器合并为一个 Composite Collider 2D 时,如果这些碰撞器原本都附加在同一个动态 Rigidbody 2D(即 Is Kinematic 不勾选)上,Composite Collider 2D 组件会接管这些碰撞器的物理属性和形状更新。这是因为动态刚体需要参与物理模拟,包括力的施加、碰撞响应等。
-
质心和旋转惯性:单个碰撞器的质心和旋转惯性是其物理属性的一部分,影响物体的物理行为。当这些碰撞器被合并时,它们的质心和旋转惯性需要被重新计算和设置,以确保物理模拟的准确性。由于 Composite Collider 2D 改变了碰撞器的几何形状,它也影响了这些物理属性。
-
显式设置:由于 Composite Collider 2D 组件改变了碰撞器的几何形状,原始碰撞器的质心和旋转惯性可能不再适用。因此,如果这些物理属性对你的游戏逻辑很重要,你可能需要在 Rigidbody 2D 组件中显式设置这些值,以确保物体的物理行为符合预期。
创建Grid下的Tilemap
也可以在palette里修改(z轴大小(当然,肯定是假的z))
在Unity中,Tilemap下的图片重叠问题通常是由于渲染顺序不正确导致的。Unity的摄像机组件会根据物体的Z轴位置(或Y轴位置,如果是2D正交摄像机)来决定渲染顺序,但当使用透明或半透明材质时,这种默认的排序方式可能会导致渲染错误,从而出现重叠问题。
为了解决这个问题,需要调整Unity的透明排序模式(Transparency Sort Mode)。在Project Settings中的Graphics设置里,找到Camera Settings,然后修改Transparency Sort Mode为Custom Axis。这样做可以让Unity根据你指定的轴来排序透明物体,而不是默认的Z轴或Y轴。
当你将Transparency Sort Axis设置为自定义轴时,你需要指定一个轴向量,Unity会根据这个轴向量来确定渲染顺序。这个轴向量通常是一个指向场景中的“前方”的向量,例如(0, 1, 0)代表Y轴正方向。
由于不同的项目可能有不同的坐标系统和视角,所以这个轴向量需要根据你的具体情况进行调整。这就是为什么设置为(0, 1, -0.26)这样的值,为了适应特定的视角和布局需求,确保Tilemap的层级按照预期的顺序渲染。
如果项目使用的是Universal Render Pipeline (URP),那么Transparency Sort Mode设置可能不可用,而是需要在URP的Render Data配置文件中进行设置
也是有不同层的显示和覆盖,Tilemap Render上有这样的层级的东西
Tilemap
不点Edit不能编辑
编辑完palette之后,再按一下Edit就是取消编辑
可以上票走人了
Unity的Tile资源随着Unity版本的更新发生了一些变化。从Unity 2017.2版本开始,Unity引入了Tilemap系统,这个系统允许开发者更高效地创建和管理2D游戏关卡。以下是一些Unity Tile资源的主要变化和特点:
-
Tilemap和Tile资源的基本组成:Unity中的Tilemap系统由Sprite(精灵)、Tile(瓦片)、Tile Palette(调色板)、Brush(笔刷)、Tilemap(瓦片地图)和Grid(网格)组成。这些组件共同工作,使得在Unity中创建和管理瓦片地图变得更加直观和高效。
-
创建瓦片资源:可以在Unity中通过两种方式创建瓦片资源。第一种方法是直接从Assets菜单创建瓦片资源。第二种方法是从一组精灵自动生成瓦片资源。
-
导入和准备精灵:将纹理放入Assets文件夹即可将瓦片地图的各个瓦片或瓦片集导入Unity项目。导入精灵时,需要设置Texture Type为Sprite (2D and UI),并且根据精灵的数量设置Sprite Mode为Single或Multiple。
-
自动生成瓦片资源:通过将瓦片的单个精灵或精灵图集直接放入Tile Palette窗口,可以自动生成瓦片。系统会提示输入新瓦片资源的保存位置,新的瓦片资源会生成并自动放置在瓦片面板上。
-
删除瓦片资源:当删除瓦片资源时,它们将被替换为占位瓦片,这有助于识别在项目中有意或无意删除的瓦片资源。
-
Tilemap的动态控制:Unity提供了动态设置瓦片、动态获取瓦片坐标、动态删除瓦片以及批量替换瓦片等功能,这些功能使得Tilemap的使用更加灵活。
-
Grid系统:Unity中的Grid系统允许开发者创建不同类型的瓦片地图,如矩形、六边形和等距瓦片地图,每种类型的瓦片地图都有其特定的使用场景和优势。
Sprite Shape Profile
很智能,很快,啪的一下碰撞器 edge就给解决了
点是单独设置的,要不要边界拐弯图片显示和点附近的高度显示
在 Unity 中,Sprite Shape Profile 是一种用于创建和管理 Sprite Shape 组件的工具。它允许开发者定义一组可复用的设置和样式,以便在场景中生成复杂的形状。下面是它的主要功能和好处:
1. 形状生成
- 灵活性:通过 Sprite Shape Profile,用户可以创建不同类型的形状,如地形、道路等。可以定义顶点、曲线和边缘,使形状更复杂。
- 动态调整:可以在运行时动态调整形状,便于快速迭代和测试。
2. 可重用性
- 集中管理:所有形状的设置(如材质、纹理、边缘等)都集中在一个地方,便于管理和修改。
- 复用设置:可以在多个 Sprite Shape 组件之间复用同一个 Profile,保持一致性并减少工作量。
3. 视觉效果
- 自定义样式:通过 Profile,可以轻松定义和应用不同的样式,比如不同的纹理和颜色,使游戏世界更具视觉吸引力。
- 边缘平滑:支持创建平滑的边缘效果,避免了不必要的图形撕裂或锯齿。
4. 与其他工具的集成
- 与 Tilemap 的兼容性:Sprite Shape 可以与 Tilemap 结合使用,生成复杂的地形和路径,适合 2D 游戏开发。
- 支持物理系统:可以与 Unity 的物理系统集成,添加碰撞体,以便物体在 Sprite Shape 上进行交互。
5. 性能优化
- 批处理渲染:使用 Sprite Shape 时,可以通过批处理减少 Draw Call,从而优化性能。
UV
UV指的是纹理坐标(Texture Coordinates),它是3D图形和2D图形中的一个概念。在3D建模和游戏开发中,UV坐标用于将2D纹理(如图像)映射到3D模型的表面,或者在2D中用于定义纹理如何贴合对象。
UV坐标的作用:
-
纹理映射:在3D模型上,每个顶点都会被分配一个UV坐标,这个坐标告诉渲染引擎如何将纹理图像映射到模型的表面。比如,UV坐标(0,0)通常对应纹理的左下角,(1,1)对应右上角。
-
控制纹理显示:通过调整UV坐标,可以控制纹理在对象上的具体显示方式,比如重复、拉伸、旋转等。
-
纹理混合:在需要将多个纹理混合显示在同一个模型上时,UV坐标可以帮助精确控制各个纹理的边界和过渡。
在Unity中UV的应用:
-
Sprite Shape Controller 中的UV设置影响纹理如何在Sprite Shape上显示。例如,
Stretch UV
选项可以让纹理填充整个Sprite Shape的轮廓,而不是按照纹理的原始宽高比。 -
Pixels Per Unit 设置影响纹理在Sprite Shape上的缩放。这个值越高,纹理在Sprite Shape上显示得越小。
-
World Space UV 设置决定纹理坐标是相对于整个世界空间还是仅限于单个GameObject。在某些情况下,你可能希望纹理根据世界空间的位置变化,而不是仅仅跟随GameObject的变换。
UV坐标是数字艺术、游戏开发和图形设计中的一个重要概念,它对于实现丰富的视觉效果和纹理映射至关重要。在Unity等游戏引擎中,理解和正确使用UV坐标可以帮助你更好地控制游戏资产的外观和行为。
Sprite Shape Controller
在Unity中,Sprite Shape Controller是一个用于编辑和控制Sprite Shape轮廓形状的组件。
将Sprite Shape Profile拖入场景时,会自动附加到创建的GameObject上。
1. **Profile**:选择用于此Sprite Shape的Sprite Shape Profile 。
2. **Edit Spline**:启用此选项可以使Sprite Shape的控制点变得可见且可编辑 。
3. **Detail**:选择渲染Sprite Shape网格的细分质量,有高、中、低质量选项 。
4. **Open Ended**:禁用此选项可以将Sprite Shape的两端连接起来形成一个封闭的形状;启用此选项则会使两端保持不连接状态 。
5. **Adaptive UV**:默认启用。启用时,Unity会尝试通过在控制点之间变形来无缝平铺Sprite,从而沿着Sprite Shape路径平铺Sprite 。
6. **Stretch UV**:启用此设置可以让Unity将填充纹理的UV拉伸到Sprite Shape的完整矩形区域 。
7. **Pixels Per Unit**:当禁用Stretch UV时,这个值会影响Sprite Shape的填充纹理的外观,影响填充纹理的缩放,值越高纹理尺寸越小,默认值为100 。
8. **World Space UV**:当禁用Stretch UV时,启用此选项会根据世界空间UV应用填充纹理,而不是每个GameObject的UV 。
要编辑Sprite Shape的网格轮廓,可以点击Edit Spline按钮,使形状的样条线和控制点变得可见且可编辑。启用Edit Spline后,可以移动Sprite Shape的控制点来调整其整体形状和大小。通过在样条线之间点击来添加额外的控制点,按下Del/Delete键可以删除当前选中的控制点。选中控制点时,可以通过按M键循环切换点模式。要改变在选中控制点处显示的Sprite Variant,可以按N键循环浏览所有可用的Variant 。
此外,Sprite Shape Controller还提供了一些额外的Collider设置,包括更新Collider、偏移量、优化Collider和细节设置,这些可以帮助你根据Sprite Shape的当前形状编辑Collider网格 。
总之,Sprite Shape Controller是一个强大的工具,可以帮助你在Unity中创建和管理复杂的2D形状。通过调整控制点、轮廓和Collider设置,你可以为你的项目创建独特的Sprite Shape效果。
线程池
在Java中,自定义线程池的工作队列需要使用`ThreadPoolExecutor`类,因为`Executors`工厂方法创建的线程池都是使用了默认的工作队列。`ThreadPoolExecutor`类提供了构造函数,允许你指定核心线程数、最大线程数、工作队列、线程工厂和拒绝策略。
以下是创建自定义工作队列的线程池的步骤:
1. 选择或实现一个`BlockingQueue`子类作为工作队列。
2. 创建一个`ThreadFactory`实例,用于创建线程。
3. 创建一个`RejectedExecutionHandler`实例,用于处理任务拒绝情况。
4. 使用`ThreadPoolExecutor`构造函数创建线程池。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class CustomThreadPoolExample {
public static void main(String[] args) {
// 自定义线程工厂
ThreadFactory threadFactory = new CustomThreadFactory("Custom-Thread-Pool-");// 自定义工作队列
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);// 自定义拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 保持活动时间
TimeUnit.SECONDS, // 时间单位
workQueue, // 工作队列
threadFactory, // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);// 提交任务到线程池
for (int i = 0; i < 15; i++) {
final int taskNo = i;
executor.execute(() -> {
System.out.println("执行任务:" + taskNo + " 线程:" + Thread.currentThread().getName());
});
}// 关闭线程池
executor.shutdown();
}static class CustomThreadFactory implements ThreadFactory {
private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}@Override
public Thread newThread(Runnable r) {
String name = namePrefix + nextId.getAndIncrement();
Thread t = new Thread(r, name);
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
}
在这个例子中,我们创建了一个`ArrayBlockingQueue`作为工作队列,它的容量为10。我们还实现了一个`CustomThreadFactory`类来创建线程。此外,我们使用了`ThreadPoolExecutor.AbortPolicy()`作为拒绝策略,这意味着如果线程池和工作队列都满了,那么任务将被拒绝,并且会抛出一个`RejectedExecutionException`。
请注意,自定义线程池时,需要仔细考虑工作队列的大小和线程池的最大线程数,以避免资源耗尽或性能问题。
callable是搭配future来实现返回值的接受
主线程亲自服务,就是callerrunsPolicy
runnable是不返回结果的任务,callable是返回结果的任务
Java中的线程池是一种执行器(Executor),用于在一个后台线程中执行任务。线程池的主要目的是减少在创建和销毁线程时所产生的性能开销。通过重用已经创建的线程来执行新的任务,线程池提高了程序的响应速度,并且提供了更好的系统资源管理。
Java的java.util.concurrent
包提供了线程池的实现,主要类是Executors
和ThreadPoolExecutor
。
以下是线程池的一些关键参数和概念:
-
核心线程数(Core Pool Size):线程池中始终保持的线程数量,即使它们处于空闲状态。
-
最大线程数(Maximum Pool Size):线程池中允许的最大线程数量。
-
工作队列(Work Queue):用于存放待执行任务的阻塞队列。
-
线程工厂(Thread Factory):用于创建新线程的工厂。
-
拒绝策略(Rejected Execution Handler):当任务太多,无法被线程池及时处理,且工作队列已满时,采取的策略。
-
保持活动时间(Keep-Alive Time):非核心线程空闲时在终止前等待新任务的最长时间。
-
时间单位(Time Unit):保持活动时间的时间单位。
Java提供了几种预定义的线程池,可以通过Executors
类方便地创建:
- CachedThreadPool:一个可根据需要创建新线程的线程池,对于短生命周期的异步任务非常合适。
- FixedThreadPool:拥有固定数量线程的线程池,适用于负载较重的服务器。
- SingleThreadExecutor:只有一个线程的线程池,保证所有任务按顺序执行。
- ScheduledThreadPool:用于延迟执行或定期执行任务的线程池。
创建线程池的示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个拥有5个工作线程的固定大小线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 提交任务到线程池
for (int i = 0; i < 10; i++) {
final int taskNo = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" + taskNo + " 线程:" + Thread.currentThread().getName());
}
});
}
// 关闭线程池
threadPool.shutdown();
}
}
在实际应用中,应该根据任务的特性和系统的资源情况来选择合适的线程池类型和参数。正确使用线程池可以显著提高程序的性能和资源利用率。
Java线程安全
同步方法public synchronized void drawMoney(double money)
synchronized()里面任意对象,只是拿来标记一下的
c++判断浮点数是否为整数
在 C++ 中,判断一个浮点数是否是整数可以使用以下方法:
1. 使用 std::floor
或 std::ceil
你可以将浮点数与其向下取整或向上取整的结果进行比较:
#include <cmath> bool isInteger(double value)
{ return std::floor(value) == value; // 或者使用
std::ceil // return std::ceil(value) == value; }
2. 使用 std::fmod
在 C++ 中,取模运算符 %
只能用于整数类型,而不能直接用于浮点数(如 float
或 double
)。如果尝试对浮点数使用 %
,编译器会报错。
不过,你可以使用 std::fmod
函数,它是 <cmath>
头文件中的一个函数,可以用于浮点数的取模运算。
使用取模运算来判断:
#include <cmath>
bool isInteger(double value)
{ return std::fmod(value, 1.0) == 0.0; }
3. 类型转换
通过将浮点数转换为整数类型,然后再比较:
#include <iostream>
bool isInteger(double value)
{ return static_cast<int>(value) == value; }
示例
#include <iostream>
#include <cmath>
bool isInteger(double value)
{ return std::floor(value) == value; // 或其他方法 }
int main()
{ double num = 4.0;
if (isInteger(num))
{ std::cout << num << " is an integer." << std::endl; }
else { std::cout << num << " is not an integer." << std::endl; } return 0; }
完全穷尽事件
互补事件是指两个事件中的一个必然发生,且它们互斥,即它们不能同时发生。例如,抛硬币的正面和反面就是互补事件,因为它们是互斥的,并且它们的并集涵盖了所有可能的结果。在概率论中,事件A和它的互补事件A'(非A)的总概率总是等于1,即P(A) + P(A') = 1。
完全穷尽和互补没什么关系,一整个事件的发生可以拆成两种情况,
这关系到你的拆分的方法,如果你选择了一个互相无关的拆法,就是互补
如果两个事件必然同时存在,并且它们一起涵盖了所有要考虑的情况,那么这两个事件可以被称为“互补且完全穷尽的事件”。在这种情况下,每个事件都是对方的必要条件,它们共同构成了整个样本空间。
例如,如果你在考虑一个骰子的投掷结果,你可以定义两个事件:
- 事件A:骰子显示的数字是奇数(1, 3, 5)。
- 事件B:骰子显示的数字是偶数(2, 4, 6)。
这两个事件是互补的,因为如果你得到了一个奇数,你就不能得到一个偶数,反之亦然。同时,它们也是完全穷尽的,因为骰子的投掷结果必然是1到6之间的一个数字,所以它必然是奇数或偶数。
在这种情况下,事件A和事件B的并集是整个样本空间(所有可能的投掷结果),而它们的交集是空集(因为它们不能同时发生)。这种关系在概率论中非常重要,因为它允许我们使用概率的加法规则来计算事件的概率。
如果两个事件的每一种情况都对应着整个事件的所有情况,并且这两个事件一起涵盖了所有要考虑的情况,那么这两个事件可以被称为“完全穷尽的事件对”。这意味着:
- 完全穷尽:这两个事件的并集包含了所有可能的情况,没有任何遗漏。
- 互不重叠:这两个事件的交集为空,即它们之间没有共同的情况。
a*b=100的所有组合情况
如果将 a 和 b 视为事件,并在概率上分析所有可能的组合情况,可以使用以下术语和概念:
1. 事件空间(Event Space)
- 定义:所有可能的事件组合构成的集合。在这里,事件空间可以表示为所有满足 a×b=100a \times b = 100a×b=100 的整数对 (a,b)(a, b)(a,b)。
2. 样本空间(Sample Space)
- 定义:所有可能的结果的集合。在此情况下,样本空间是所有符合 a×b=100a \times b = 100a×b=100 的整数对。
3. 组合事件(Combination Event)
- 定义:特定情况下的事件组合。例如,可以定义事件 EEE 为“获取所有符合 a×b=100a \times b = 100a×b=100 的整数对”。
4. 计数(Counting)
- 定义:对样本空间中的事件进行计数。在本例中,可以计算满足条件的所有整数因子对的数量。
5. 概率(Probability)
- 定义:某一特定事件发生的可能性。可以通过 P(E)=事件数样本空间大小P(E) = \frac{\text{事件数}}{\text{样本空间大小}}P(E)=样本空间大小事件数 来计算特定组合事件的概率。
示例
对于 aaa 和 bbb 的组合情况:
- 样本空间 S={(1,100),(2,50),(4,25),(5,20),(10,10)}S = \{(1, 100), (2, 50), (4, 25), (5, 20), (10, 10)\}S={(1,100),(2,50),(4,25),(5,20),(10,10)}
- 计算样本空间的大小:在此例中,样本空间大小为 5(即 5 对整数因子)。
6. 条件概率(Conditional Probability)
- 如果你想要分析某个特定条件下的组合,例如 aaa 的某个范围内的组合,可以使用条件概率 P(A∣C)P(A | C)P(A∣C),其中 CCC 是限制条件。
c++开三次方
在C++标准库中,没有直接的开三次方(立方根)函数,但你可以使用std::pow
函数来实现。std::pow
是用来计算任意底数的任意次方的函数,虽然它不是专门针对立方根的,但你可以通过传入参数1.0 / 3.0
来计算立方根。
#include <iostream>
#include <cmath> // 需要包含这个头文件
int main() {
double number = 27.0;
double cubeRoot = std::pow(number, 1.0 / 3.0); // 计算立方根
std::cout << "The cube root of " << number << " is " << cubeRoot << std::endl;
return 0;
}
想休息了,就休息