- 在场景中移动
Unity的坐标系统
Unity使用笛卡尔坐标系来描述物体坐标信息,笛卡尔坐标系分为左手坐标系和右手坐标系两种。Unity使用的是笛卡尔左手坐标系。
注意:这里与Blender前视图的坐标系不同
之前测试将Blender中创建的模型导入Unity中也确实发生了问题,在场景中的显示是正常的,但是添加角色移动的脚本以后操作起来会有问题。
看起来Blender的顶视图和Unity的坐标系是可以对应的。也确实在网络上看到了对类似问题的描述和解决办法。
大体上是需要将模型的x轴旋转-90°,然后Ctrl + A将该属性应用。再在此应用的基础上延x轴旋转90°调整回来。
后续会测试一下这种方法是否有效。
场景视图坐标工具
单击任何锥形轴臂会将 Scene 视图摄像机对齐到该锥形轴臂所代表的轴(例如:顶视图、左视图和前视图)。还可以右键单击该立方体来显示一个包含视角列表的菜单。要返回默认视角,请右键单击场景视图辅助图标,然后单击 Free。
导航工具
方向键:激活场景窗口后可以使用方向键在场景中移动,按住Shift可以加速
单击Q切换到查看工具状态下:
按住左键拖动,可以在场景中移动
按住Alt再按住左键拖动,可以围绕当前轴心点旋转视角
鼠标滚轮:可缩放
在场景中按住鼠标右键:
可使用WASD向左/向右/向前/向后移动,使用 Q 和 E 向上和向下移动
聚焦指定对象:选中对象后,点 F。要将视图锁定到游戏对象(即使游戏对象正在移动),按 Shift+F。 - 选择游戏对象
可以在场景中选择,也可以在层级中选择。
可以设置对象是否允许被选取,也可以设置对象是否显示。但要注意设置为不显示的对象依然可以被选取,所以一般情况下在设置不显示的同时也会将其设置为不可选取。
Unity软件界面
2个视图:
场景视图(Sence):编辑场景的地方,基本上游戏中能看到的所有元素都包含在场景中,可以通过 场景视图来编辑、操作
游戏视图(Game):游戏执行的效果展示
3个窗口:
项目窗口(project):资源库
层级窗口(Hierachy):游戏对象按层次展示在该窗口中,可以快速定位我们要操作的对象,并清晰展示对象间关系
检查器窗口(Inspector):可以查看并配置选中对象的各个属性,选中的对象不同,布局和内容也不同
常用的Blender快捷键
快捷键 | 功能 |
A | 全选 |
X | 删除 |
Shift + A | 添加 |
Shift + D | 复制 |
Shift + C | 3D游标回到原点 |
G | 随意移动 |
GX GY GZ | 在指定坐标轴上移动 |
GX3 | 在x轴上移动3格 |
R | 旋转 |
RX90 | 沿x轴旋转90度 |
S | 缩放 |
/ | 隔离模式 |
Tab | 进入编辑模式 |
编辑模式下 1,2,3 | 点,线,面 |
Ctrl + L | 关联 |
Ctrl + P | 创建父级 |
Alt + P | 清空父级 |
Ctrl + I | 反向选择 |
E | 挤出 |
Alt + E | 沿法向挤出 |
姿态模式下 Alt + G Alt + R Alt + S | 骨架复位 位置 旋转 缩放 |
Ctrl + A | 应用(缩放等) |
在选项上点击右键 | 加入快速收藏夹 |
Q | 打开快速收藏夹 |
M | 添加集合 |
Alt + Z | 透明模式 |
Ctrl + R 滚动鼠标滚轮 | 插入循环边 调整循环边数量 |
编辑模式下2 Alt + 左键 | 选中循环边 |
Ctrl + B | 倒角 |
编辑模式下 U | UV映射 |
新装Blender时需要做的一些调整
选择渲染设备,设定更高的全局撤销次数
方便在主界面移动物体
Ubuntu安装Gitlab
1.安装和配置必要的依赖项
sudo apt-get update sudo apt-get install -y curl openssh-server ca-certificates tzdata perl sudo apt-get install -y postfix
2.添加GitLab包仓库并安装包
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo apt-get install gitlab-ce
3.设置浏览器要访问的IP地址及端口号
编辑配置文件
vim /etc/gitlab/gitlab.rb
4.重新载入配置及启动服务
gitlab-ctl reconfigure
gitlab-ctl start
5.查找Gitlab默认root密码
vim /etc/gitlab/initial_root_password
注:该文件为临时文件,将于reconfigure后的24小时后自动删除,注意尽早保存。
静态链接DLL与动态调用DLL
静态链接DLL:
需要设置“附加包含目录”,“附加库目录”以及“附加依赖项”。
然后通过外部引用函数定义即可。
#include <stdio.h> #include <Windows.h> extern int add(int a, int b); int main(int argc, char* argv[]) { MessageBoxW(NULL, L"Test", L"Test", MB_OK); printf("5 + 3 = %d", add(5, 3)); return 0; }
动态调用DLL:
即通过函数调用的方式获得所需函数的地址,然后通过函数指针的方式调用函数。
#include <stdio.h> #include <Windows.h> int main(int argc, char* argv[]) { HMODULE load_lib = NULL; FARPROC get_proc_add = 0; MessageBoxW(NULL, L"Test", L"Test", MB_OK); do { load_lib = LoadLibraryW(L"C:\\Users\\Raiseki\\source\\repos\\dll_learn\\x64\\Debug\\dll_learn.dll"); if (load_lib == NULL) { break; } get_proc_add = GetProcAddress(load_lib, "add"); if (get_proc_add == NULL) { break; } printf("5 + 3 = %lld", get_proc_add(5, 3)); } while (0); return 0; }
注意:
当找不到DLL文件时,采用静态链接方式的程序系统会报错且无法运行。而采用动态调用方式的程序虽然无法调用到所需函数,但系统不会报错。
C语言控制台程序编写Windows服务(二)
在C语言控制台程序编写Windows服务(一)的基础上进行了改动。对比上次,本次用到了Event(事件)和父子进程的概念,解决了服务在系统重启后无法随系统自动启动的问题。
目标回顾:编写一个服务,能够正常添加到Windows服务列表并能随系统启动,可正常手动启停。暂时不需要通过服务实现任何实际的功能。
修改过程中明确了以下几点:
1. 明确原理,仔细阅读文档说明。
以此方式写exe服务在exe文件启动时会存在两个进程。一个控制台程序的进程以及一个服务进程。
2. 明确需要做的工作。同时满足以下两件事。
(1)判断服务进程是否已经存在。如果不存在则通过控制台程序进程创建服务并启动,否则退出,不要让程序运行到StartServiceCtrlDispatcherW。
(2)要避免服务进程重复创建进程导致程序出错。这就需要能够判断当前进程是控制台程序进程还是服务进程。服务进程的特点是它们都是services.exe的子进程,所以可以通过判断当前进程的父进程是否为service.exe的方式来对两种进程进行区分。方法如下:
获取进程pid,ppid以及进程名
3.今后编写代码要注意清晰简洁。大量无意义的代码有时会让自己陷入混乱。
以下为修改后的代码:
#include <stdio.h> #include <Windows.h> #include <tlhelp32.h> SERVICE_STATUS service_status; SERVICE_STATUS_HANDLE service_status_handle; BOOL GetProcessInfoWithPID(DWORD pid, DWORD* ppid, WCHAR* process_name) { BOOL result = FALSE; HANDLE snapshot_handle = NULL; PROCESSENTRY32W process_entry32; BOOL process_return = FALSE; do { snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot_handle == INVALID_HANDLE_VALUE) { break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32FirstW(snapshot_handle, &process_entry32); while (process_return) { if (process_entry32.th32ProcessID == pid) { if (ppid != NULL) { *ppid = process_entry32.th32ParentProcessID; } if (process_name != NULL) { lstrcpyW(process_name, process_entry32.szExeFile); } result = TRUE; break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32NextW(snapshot_handle, &process_entry32); } } while (0); if (snapshot_handle) { CloseHandle(snapshot_handle); } return result; } BOOL ServiceStart(HANDLE handle) { BOOL result = FALSE; SERVICE_STATUS_PROCESS status; DWORD bytes_need = 0; SecureZeroMemory(&status, sizeof(status)); do { result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } else if ((status.dwCurrentState == SERVICE_RUNNING) || (status.dwCurrentState == SERVICE_START_PENDING)) { service_status.dwCurrentState = SERVICE_RUNNING; result = TRUE; break; } result = StartServiceW(handle, 0, NULL); if (result == FALSE) { break; } printf("please wait...\n"); do { result = FALSE; Sleep(500); result = QueryServiceStatusEx(handle, SC_STATUS_PROCESS_INFO, (LPBYTE)&status, sizeof(SERVICE_STATUS_PROCESS), &bytes_need); if (result == FALSE) { break; } } while (status.dwCurrentState == SERVICE_START_PENDING); result = FALSE; if (status.dwCurrentState == SERVICE_RUNNING) { result = TRUE; break; } } while (0); return result; } BOOL InstallService() { BOOL result = FALSE; SC_HANDLE SCM_handle = NULL; SC_HANDLE service_handle = NULL; DWORD return_value = 0; TCHAR Path[MAX_PATH]; do { return_value = GetModuleFileNameW(NULL, Path, MAX_PATH); if (return_value == 0) { break; } SCM_handle = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (SCM_handle == NULL) { break; } service_handle = CreateServiceW( SCM_handle, L"TestService", L"TestService", SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, Path, NULL, NULL, NULL, NULL, NULL ); if (service_handle == NULL) { break; } result = ServiceStart(service_handle); if (result == FALSE) { break; } result = TRUE; } while (0); if (SCM_handle != NULL) { CloseServiceHandle(SCM_handle); } if (service_handle != NULL) { CloseServiceHandle(service_handle); } return result; } VOID WINAPI ServiceControlHandler(DWORD parameter) { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; if (parameter == SERVICE_CONTROL_STOP) { service_status.dwCurrentState = SERVICE_STOPPED; } SetServiceStatus(service_status_handle, &service_status); return; } VOID WINAPI ServiceMain(DWORD argc, LPWSTR* argv) { service_status_handle = RegisterServiceCtrlHandlerW(L"TestService", (LPHANDLER_FUNCTION)ServiceControlHandler); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = 0; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 0; SetServiceStatus(service_status_handle, &service_status); while (1) { Sleep(10000); OutputDebugStringA("TestService Running"); } return; } int main(int argc, char* argv[]) { BOOL result = 0; HANDLE event_handle = NULL; DWORD pid = 0; DWORD ppid = 0; WCHAR process_name[MAX_PATH]; int wcscmp_return; SERVICE_TABLE_ENTRY service_table[2]; SecureZeroMemory(process_name, sizeof(process_name)); do { event_handle = CreateEventW(NULL, FALSE, TRUE, L"Global\\Test"); if (event_handle == NULL) { break; } pid = GetCurrentProcessId(); result = GetProcessInfoWithPID(pid, &ppid, process_name); if (result == FALSE) { break; } result = GetProcessInfoWithPID(ppid, 0, process_name); if (result == FALSE) { break; } wcscmp_return = lstrcmpiW(process_name, L"services.exe"); if (wcscmp_return != 0) { if (GetLastError() == ERROR_ALREADY_EXISTS) { break; } result = InstallService(); if (result == 0) { break; } break; } service_table[0].lpServiceName = L"TestService"; service_table[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain; service_table[1].lpServiceName = NULL; service_table[1].lpServiceProc = NULL; result = StartServiceCtrlDispatcherW(service_table); if (result == 0) { break; } } while (0); if (event_handle != NULL) { CloseHandle(event_handle); } return 0; }
获取进程pid,ppid以及进程名
获取pid:
pid = GetCurrentProcessId();
获取ppid和进程名:
BOOL GetProcessInfoWithPID(DWORD pid, DWORD* ppid, WCHAR* process_name) { BOOL result = FALSE; HANDLE snapshot_handle = NULL; PROCESSENTRY32W process_entry32; BOOL process_return = FALSE; do { snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot_handle == INVALID_HANDLE_VALUE) { break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32FirstW(snapshot_handle, &process_entry32); while (process_return) { if (process_entry32.th32ProcessID == pid) { if (ppid != NULL) { *ppid = process_entry32.th32ParentProcessID; } if (process_name != NULL) { lstrcpyW(process_name, process_entry32.szExeFile); } result = TRUE; break; } process_entry32.dwSize = sizeof(process_entry32); process_return = Process32NextW(snapshot_handle, &process_entry32); } } while (0); if (snapshot_handle) { CloseHandle(snapshot_handle); } return result; }
该函数的第二个或第三个参数如不需要可以填零。
创建事件CreateEventW
函数原型:
CreateEventW( _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, _In_ BOOL bInitialState, _In_opt_ LPCWSTR lpName );
参数1:安全描述符
参数2:手动复位
TRUE为手动,FALSE为自动。影响等待线程释放后,线程是手动还是自动的回到复位状态(即无信号状态)。
参数3:初始状态
TRUE为置位(有信号),FALSE为复位(无信号)。
参数4:事件对象名称
可设置一个最长为MAX_PATH的时间对象名称。
可以选择本地或全局(Local或Global),
如:CreateEvent( NULL, FALSE, FALSE, “Global\\CSAPP” );
利用全局内核对象命名空间可以做到进程间的同步。
也可以活用此功能避免运行重复的进程。
返回值:
如果函数成功,则返回值是事件对象的句柄。如果命名事件对象在函数调用之前存在,则该函数返回现有对象的句柄,而 GetLastError返回 ERROR_ALREADY_EXISTS。
如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。
事件(Event)
多用于线程同步。
可能用到的函数:
1.CreateEventW
HANDLE CreateEventW( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw
2.WaitForSingleObject
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
3.SetEvent
BOOL SetEvent( HANDLE hEvent );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent
4.ResetEvent
BOOL ResetEvent( HANDLE hEvent );
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-resetevent
5.SetEventWhenCallbackReturns
void SetEventWhenCallbackReturns( PTP_CALLBACK_INSTANCE pci, HANDLE evt );