1
0

增加 第一人称镜头、视角锁定镜头 (#9)

* 增加 第一人称镜头、视角锁定镜头

* Follow 摄像机增加平滑 Y 轴模式; 支持 LookAt 偏移
This commit is contained in:
chinosk 2024-06-04 09:12:30 -05:00 committed by GitHub
parent c5fd80daa2
commit 6a7717a45e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 768 additions and 88 deletions

View File

@ -11,6 +11,7 @@
#include "shadowhook.h"
#include <jni.h>
#include <thread>
#include <map>
std::unordered_set<void*> hookedStubs{};
@ -85,14 +86,18 @@ namespace GakumasLocal::HookMain {
Log::LogUnityLog(ANDROID_LOG_VERBOSE, "Internal_Log:\n%s", content->ToString().c_str());
}
bool IsNativeObjectAlive(void* obj) {
static UnityResolve::Method* IsNativeObjectAliveMtd = NULL;
if (!IsNativeObjectAliveMtd) IsNativeObjectAliveMtd = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", "UnityEngine",
"Object", "IsNativeObjectAlive");
return IsNativeObjectAliveMtd->Invoke<bool>(obj);
}
UnityResolve::UnityType::Camera* mainCameraCache = nullptr;
UnityResolve::UnityType::Transform* cameraTransformCache = nullptr;
void CheckAndUpdateMainCamera() {
if (!Config::enableFreeCamera) return;
static auto IsNativeObjectAlive = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", "UnityEngine",
"Object", "IsNativeObjectAlive");
if (IsNativeObjectAlive->Invoke<bool>(mainCameraCache)) return;
if (IsNativeObjectAlive(mainCameraCache)) return;
mainCameraCache = UnityResolve::UnityType::Camera::GetMain();
cameraTransformCache = mainCameraCache->GetTransform();
@ -137,44 +142,90 @@ namespace GakumasLocal::HookMain {
return Unity_get_fieldOfView_Orig(_this);
}
UnityResolve::UnityType::Transform* cacheTrans = NULL;
UnityResolve::UnityType::Quaternion cacheRotation{};
UnityResolve::UnityType::Vector3 cachePosition{};
UnityResolve::UnityType::Vector3 cacheForward{};
UnityResolve::UnityType::Vector3 cacheLookAt{};
DEFINE_HOOK(void, Unity_set_rotation_Injected, (UnityResolve::UnityType::Transform* _this, UnityResolve::UnityType::Quaternion* value)) {
if (Config::enableFreeCamera) {
static auto lookat_injected = reinterpret_cast<void (*)(void*_this,
UnityResolve::UnityType::Vector3* worldPosition, UnityResolve::UnityType::Vector3* worldUp)>(
Il2cppUtils::il2cpp_resolve_icall(
"UnityEngine.Transform::Internal_LookAt_Injected(UnityEngine.Vector3&,UnityEngine.Vector3&)"));
static auto worldUp = UnityResolve::UnityType::Vector3(0, 1, 0);
if (cameraTransformCache == _this) {
const auto cameraMode = GKCamera::GetCameraMode();
if (cameraMode == GKCamera::CameraMode::FIRST_PERSON) {
if (cacheTrans && IsNativeObjectAlive(cacheTrans)) {
if (GKCamera::GetFirstPersonRoll() == GKCamera::FirstPersonRoll::ENABLE_ROLL) {
*value = cacheRotation;
}
else {
lookat_injected(_this, &cacheLookAt, &worldUp);
return;
}
}
}
else if (cameraMode == GKCamera::CameraMode::FOLLOW) {
auto newLookAtPos = GKCamera::CalcFollowModeLookAt(cachePosition,
GKCamera::followPosOffset, true);
lookat_injected(_this, &newLookAtPos, &worldUp);
return;
}
else {
auto& origCameraLookat = GKCamera::baseCamera.lookAt;
lookat_injected(_this, &origCameraLookat, &worldUp);
// Log::DebugFmt("fov: %f, target: %f", Unity_get_fieldOfView_Orig(mainCameraCache), GKCamera::baseCamera.fov);
return;
}
}
}
return Unity_set_rotation_Injected_Orig(_this, value);
}
DEFINE_HOOK(void, Unity_set_position_Injected, (UnityResolve::UnityType::Transform* _this, UnityResolve::UnityType::Vector3* data)) {
if (Config::enableFreeCamera) {
CheckAndUpdateMainCamera();
if (cameraTransformCache == _this) {
//Log::DebugFmt("MainCamera set pos: %f, %f, %f", data->x, data->y, data->z);
auto& origCameraPos = GKCamera::baseCamera.pos;
data->x = origCameraPos.x;
data->y = origCameraPos.y;
data->z = origCameraPos.z;
const auto cameraMode = GKCamera::GetCameraMode();
if (cameraMode == GKCamera::CameraMode::FIRST_PERSON) {
if (cacheTrans && IsNativeObjectAlive(cacheTrans)) {
*data = GKCamera::CalcFirstPersonPosition(cachePosition, cacheForward, GKCamera::firstPersonPosOffset);
}
}
else if (cameraMode == GKCamera::CameraMode::FOLLOW) {
auto newLookAtPos = GKCamera::CalcFollowModeLookAt(cachePosition, GKCamera::followPosOffset);
auto pos = GKCamera::CalcPositionFromLookAt(newLookAtPos, GKCamera::followPosOffset);
data->x = pos.x;
data->y = pos.y;
data->z = pos.z;
}
else {
//Log::DebugFmt("MainCamera set pos: %f, %f, %f", data->x, data->y, data->z);
auto& origCameraPos = GKCamera::baseCamera.pos;
data->x = origCameraPos.x;
data->y = origCameraPos.y;
data->z = origCameraPos.z;
}
}
}
return Unity_set_position_Injected_Orig(_this, data);
}
DEFINE_HOOK(void, Unity_set_rotation_Injected, (UnityResolve::UnityType::Transform* _this, UnityResolve::UnityType::Quaternion* value)) {
if (Config::enableFreeCamera) {
if (cameraTransformCache == _this) {
auto& origCameraLookat = GKCamera::baseCamera.lookAt;
static auto lookat_injected = reinterpret_cast<void (*)(void*_this,
UnityResolve::UnityType::Vector3* worldPosition, UnityResolve::UnityType::Vector3* worldUp)>(
Il2cppUtils::il2cpp_resolve_icall(
"UnityEngine.Transform::Internal_LookAt_Injected(UnityEngine.Vector3&,UnityEngine.Vector3&)"));
static auto worldUp = UnityResolve::UnityType::Vector3(0, 1, 0);
lookat_injected(_this, &origCameraLookat, &worldUp);
// Log::DebugFmt("fov: %f, target: %f", Unity_get_fieldOfView_Orig(mainCameraCache), GKCamera::baseCamera.fov);
return;
}
}
return Unity_set_rotation_Injected_Orig(_this, value);
}
DEFINE_HOOK(void, EndCameraRendering, (void* ctx, void* camera, void* method)) {
EndCameraRendering_Orig(ctx, camera, method);
if (Config::enableFreeCamera && mainCameraCache) {
Unity_set_fieldOfView_Orig(mainCameraCache, GKCamera::baseCamera.fov);
if (GKCamera::GetCameraMode() == GKCamera::CameraMode::FIRST_PERSON) {
mainCameraCache->SetNearClipPlane(0.001f);
}
}
}
@ -243,10 +294,8 @@ namespace GakumasLocal::HookMain {
"UnityEngine", "Font");
static auto Font_ctor = Il2cppUtils::GetMethod("UnityEngine.TextRenderingModule.dll",
"UnityEngine", "Font", ".ctor");
static auto IsNativeObjectAlive = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", "UnityEngine",
"Object", "IsNativeObjectAlive");
if (fontCache) {
if (IsNativeObjectAlive->Invoke<bool>(fontCache)) {
if (IsNativeObjectAlive(fontCache)) {
return fontCache;
}
}
@ -390,10 +439,10 @@ namespace GakumasLocal::HookMain {
PictureBookLiveThumbnailView_SetData_Orig(_this, liveData, isUnlocked, isNew);
}
bool needRestoreHides = false;
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* _this, void* produceLive,
Il2cppString* characterId, Il2cppString* costumeId, Il2cppString* costumeHeadId)) {
needRestoreHides = false;
Log::InfoFmt("MoveLiveScene: characterId: %s, costumeId: %s, costumeHeadId: %s,",
characterId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
@ -517,6 +566,136 @@ namespace GakumasLocal::HookMain {
return VLUtility_GetLimitedResolution_Orig(screenWidth, screenHeight, aspectRatio, maxBufferPixel, bufferScale, firstCall);
}
DEFINE_HOOK(void, CampusActorModelParts_OnRegisterBone, (void* _this, Il2cppString** name, UnityResolve::UnityType::Transform* bone)) {
CampusActorModelParts_OnRegisterBone_Orig(_this, name, bone);
// Log::DebugFmt("CampusActorModelParts_OnRegisterBone: %s, %p", (*name)->ToString().c_str(), bone);
}
bool InitBodyParts() {
static auto isInit = false;
if (isInit) return true;
const auto Enum_GetValues = Il2cppUtils::GetMethod("mscorlib.dll", "System", "Enum", "GetValues");
const auto Enum_GetNames = Il2cppUtils::GetMethod("mscorlib.dll", "System", "Enum", "GetNames");
const auto HumanBodyBones_klass = Il2cppUtils::GetClass(
"UnityEngine.AnimationModule.dll", "UnityEngine", "HumanBodyBones");
const auto values = Enum_GetValues->Invoke<UnityResolve::UnityType::Array<int>*>(HumanBodyBones_klass->GetType())->ToVector();
const auto names = Enum_GetNames->Invoke<UnityResolve::UnityType::Array<Il2cppString*>*>(HumanBodyBones_klass->GetType())->ToVector();
if (values.size() != names.size()) {
Log::ErrorFmt("InitBodyParts Error: values count: %ld, names count: %ld", values.size(), names.size());
return false;
}
std::vector<std::string> namesVec{};
for (auto i :names) {
namesVec.push_back(i->ToString());
}
GKCamera::bodyPartsEnum = Misc::CSEnum(namesVec, values);
GKCamera::bodyPartsEnum.SetIndex(GKCamera::bodyPartsEnum.GetValueByName("Head"));
isInit = true;
return true;
}
void HideHead(UnityResolve::UnityType::GameObject* obj, const bool isFace) {
static UnityResolve::UnityType::GameObject* lastFaceObj = nullptr;
static UnityResolve::UnityType::GameObject* lastHairObj = nullptr;
#define lastHidedObj (isFace ? lastFaceObj : lastHairObj)
static auto get_activeInHierarchy = reinterpret_cast<bool (*)(void*)>(
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.GameObject::get_activeInHierarchy()"));
const auto isFirstPerson = GKCamera::GetCameraMode() == GKCamera::CameraMode::FIRST_PERSON;
if (isFirstPerson && obj) {
if (obj == lastHidedObj) return;
if (lastHidedObj && IsNativeObjectAlive(lastHidedObj) && get_activeInHierarchy(lastHidedObj)) {
lastHidedObj->SetActive(true);
}
if (IsNativeObjectAlive(obj)) {
obj->SetActive(false);
lastHidedObj = obj;
}
}
else {
if (lastHidedObj && IsNativeObjectAlive(lastHidedObj)) {
lastHidedObj->SetActive(true);
lastHidedObj = nullptr;
}
}
}
DEFINE_HOOK(void, CampusActorController_LateUpdate, (void* _this, void* mtd)) {
if (!Config::enableFreeCamera || (GKCamera::GetCameraMode() == GKCamera::CameraMode::FREE)) {
if (needRestoreHides) {
needRestoreHides = false;
HideHead(NULL, false);
HideHead(NULL, true);
}
return CampusActorController_LateUpdate_Orig(_this, mtd);
}
static auto CampusActorController_klass = Il2cppUtils::GetClass("campus-submodule.Runtime.dll",
"Campus.Common", "CampusActorController");
static auto rootBody_field = CampusActorController_klass->Get<UnityResolve::Field>("_rootBody");
static auto parentKlass = UnityResolve::Invoke<void*>("il2cpp_class_get_parent", CampusActorController_klass->address);
static auto GetHumanBodyBoneTransform_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(parentKlass, "GetHumanBodyBoneTransform", 1);
static auto GetHumanBodyBoneTransform = reinterpret_cast<UnityResolve::UnityType::Transform* (*)(void*, int)>(
GetHumanBodyBoneTransform_mtd->methodPointer
);
static auto get_index_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(CampusActorController_klass->address, "get_index", 0);
static auto get_Index = get_index_mtd ? reinterpret_cast<int (*)(void*)>(
get_index_mtd->methodPointer) : [](void*){return 0;};
const auto currIndex = get_Index(_this);
if (currIndex == GKCamera::followCharaIndex) {
static auto initPartsSuccess = InitBodyParts();
static auto headBodyId = initPartsSuccess ? GKCamera::bodyPartsEnum.GetValueByName("Head") : 0xA;
const auto isFirstPerson = GKCamera::GetCameraMode() == GKCamera::CameraMode::FIRST_PERSON;
auto targetTrans = GetHumanBodyBoneTransform(_this,
isFirstPerson ? headBodyId : GKCamera::bodyPartsEnum.GetCurrent().second);
if (targetTrans) {
cacheTrans = targetTrans;
cacheRotation = cacheTrans->GetRotation();
cachePosition = cacheTrans->GetPosition();
cacheForward = cacheTrans->GetForward();
cacheLookAt = cacheTrans->GetPosition() + cacheTrans->GetForward() * 3;
auto rootBody = Il2cppUtils::ClassGetFieldValue<UnityResolve::UnityType::Transform*>(_this, rootBody_field);
auto rootModel = rootBody->GetParent();
auto rootModelChildCount = rootModel->GetChildCount();
for (int i = 0; i < rootModelChildCount; i++) {
auto rootChild = rootModel->GetChild(i);
const auto childName = rootChild->GetName();
if (childName == "Root_Face") {
for (int n = 0; n < rootChild->GetChildCount(); n++) {
auto vLSkinningRenderer = rootChild->GetChild(n);
if (vLSkinningRenderer->GetName() == "VLSkinningRenderer") {
HideHead(vLSkinningRenderer->GetGameObject(), true);
needRestoreHides = true;
}
}
}
else if (childName == "Root_Hair") {
HideHead(rootChild->GetGameObject(), false);
needRestoreHides = true;
}
}
}
else {
cacheTrans = NULL;
}
}
CampusActorController_LateUpdate_Orig(_this, mtd);
}
void StartInjectFunctions() {
const auto hookInstaller = Plugin::GetInstance().GetHookInstaller();
UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW), UnityResolve::Mode::Il2Cpp);
@ -589,6 +768,20 @@ namespace GakumasLocal::HookMain {
"VLUtility", "GetLimitedResolution",
{"*", "*", "*", "*", "*", "*"}));
ADD_HOOK(CampusActorModelParts_OnRegisterBone,
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusActorModelParts", "OnRegisterBone"));
ADD_HOOK(CampusActorController_LateUpdate,
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusActorController", "LateUpdate"));
static auto CampusActorController_klass = Il2cppUtils::GetClass("campus-submodule.Runtime.dll",
"Campus.Common", "CampusActorController");
for (const auto& i : CampusActorController_klass->methods) {
Log::DebugFmt("CampusActorController.%s at %p", i->name.c_str(), i->function);
}
ADD_HOOK(CampusQualityManager_set_TargetFrameRate,
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "set_TargetFrameRate"));

View File

@ -125,6 +125,9 @@ namespace Il2cppUtils {
return static_cast<Il2CppClassHead*>(*static_cast<void* const*>(std::assume_aligned<alignof(void*)>(instance)));
}
MethodInfo* il2cpp_class_get_method_from_name(void* klass, const char* name, int argsCount) {
return UnityResolve::Invoke<MethodInfo*>("il2cpp_class_get_method_from_name", klass, name, argsCount);
}
void* find_nested_class(void* klass, std::predicate<void*> auto&& predicate)
{
@ -147,4 +150,15 @@ namespace Il2cppUtils {
});
}
template <typename RType>
auto ClassGetFieldValue(void* obj, UnityResolve::Field* field) -> RType {
return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset);
}
template <typename RType>
auto ClassSetFieldValue(void* obj, UnityResolve::Field* field, RType value) -> void {
return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset) = value;
}
}

View File

@ -1,6 +1,6 @@
#include "Log.h"
#include <android/log.h>
#include <Misc.h>
#include <Misc.hpp>
#include <sstream>
#include <string>
#include <thread>

View File

@ -1,4 +1,4 @@
#include "Misc.h"
#include "Misc.hpp"
#include <codecvt>
#include <locale>
@ -31,4 +31,68 @@ namespace GakumasLocal::Misc {
return env;
}
} // namespace UmaPyogin::Misc
CSEnum::CSEnum(const std::string& name, const int value) {
this->Add(name, value);
}
CSEnum::CSEnum(const std::vector<std::string>& names, const std::vector<int>& values) {
if (names.size() != values.size()) return;
this->names = names;
this->values = values;
}
int CSEnum::GetIndex() {
return currIndex;
}
void CSEnum::SetIndex(int index) {
if (index < 0) return;
if (index + 1 >= values.size()) return;
currIndex = index;
}
int CSEnum::GetTotalLength() {
return values.size();
}
void CSEnum::Add(const std::string &name, const int value) {
this->names.push_back(name);
this->values.push_back(value);
}
std::pair<std::string, int> CSEnum::GetCurrent() {
return std::make_pair(names[currIndex], values[currIndex]);
}
std::pair<std::string, int> CSEnum::Last() {
const auto maxIndex = this->GetTotalLength() - 1;
if (currIndex <= 0) {
currIndex = maxIndex;
}
else {
currIndex--;
}
return this->GetCurrent();
}
std::pair<std::string, int> CSEnum::Next() {
const auto maxIndex = this->GetTotalLength() - 1;
if (currIndex >= maxIndex) {
currIndex = 0;
}
else {
currIndex++;
}
return this->GetCurrent();
}
int CSEnum::GetValueByName(const std::string &name) {
for (int i = 0; i < names.size(); i++) {
if (names[i].compare(name) == 0) {
return values[i];
}
}
return values[0];
}
}

View File

@ -1,18 +0,0 @@
#ifndef GAKUMAS_LOCALIFY_MISC_H
#define GAKUMAS_LOCALIFY_MISC_H
#include <string>
#include <string_view>
#include <jni.h>
namespace GakumasLocal {
using OpaqueFunctionPointer = void (*)();
namespace Misc {
std::u16string ToUTF16(const std::string_view& str);
std::string ToUTF8(const std::u16string_view& str);
JNIEnv* GetJNIEnv();
}
}
#endif //GAKUMAS_LOCALIFY_MISC_H

View File

@ -0,0 +1,76 @@
#pragma once
#include <string>
#include <string_view>
#include <jni.h>
#include <deque>
#include <numeric>
namespace GakumasLocal {
using OpaqueFunctionPointer = void (*)();
namespace Misc {
std::u16string ToUTF16(const std::string_view& str);
std::string ToUTF8(const std::u16string_view& str);
JNIEnv* GetJNIEnv();
class CSEnum {
public:
CSEnum(const std::string& name, const int value);
CSEnum(const std::vector<std::string>& names, const std::vector<int>& values);
int GetIndex();
void SetIndex(int index);
int GetTotalLength();
void Add(const std::string& name, const int value);
std::pair<std::string, int> GetCurrent();
std::pair<std::string, int> Last();
std::pair<std::string, int> Next();
int GetValueByName(const std::string& name);
private:
int currIndex = 0;
std::vector<std::string> names{};
std::vector<int> values{};
};
template <typename T>
class FixedSizeQueue {
static_assert(std::is_arithmetic<T>::value, "T must be an arithmetic type");
public:
FixedSizeQueue(size_t maxSize) : maxSize(maxSize), sum(0) {}
void Push(T value) {
if (deque.size() >= maxSize) {
sum -= deque.front();
deque.pop_front();
}
deque.push_back(value);
sum += value;
}
float Average() {
if (deque.empty()) {
return 0.0;
}
return static_cast<float>(sum) / deque.size();
}
private:
std::deque<T> deque;
size_t maxSize;
T sum;
};
}
}

View File

@ -1,7 +1,7 @@
#ifndef GAKUMAS_LOCALIFY_PLUGIN_H
#define GAKUMAS_LOCALIFY_PLUGIN_H
#include "Misc.h"
#include "Misc.hpp"
#include <string>
#include <memory>
#include <jni.h>

View File

@ -1,5 +1,7 @@
#include "baseCamera.hpp"
#include "camera.hpp"
#include <thread>
#include "Misc.hpp"
#define KEY_W 51
#define KEY_S 47
@ -8,11 +10,12 @@
#define KEY_R 46
#define KEY_Q 45
#define KEY_E 33
#define KEY_F 34
#define KEY_I 37
#define KEY_K 39
#define KEY_J 38
#define KEY_L 40
#define KEY_R 46
#define KEY_V 50
#define KEY_UP 19
#define KEY_DOWN 20
#define KEY_LEFT 21
@ -28,42 +31,139 @@
namespace GKCamera {
BaseCamera::Camera baseCamera{};
CameraMode cameraMode = CameraMode::FREE;
FirstPersonRoll firstPersonRoll = FirstPersonRoll::ENABLE_ROLL;
FollowModeY followModeY = FollowModeY::SMOOTH_Y;
bool rMousePressFlg = false;
UnityResolve::UnityType::Vector3 firstPersonPosOffset{0, 0.064f, 0.000f};
UnityResolve::UnityType::Vector3 followPosOffset{0, 0, 1.5};
UnityResolve::UnityType::Vector2 followLookAtOffset{0, 0};
float offsetMoveStep = 0.008;
int followCharaIndex = 0;
GakumasLocal::Misc::CSEnum bodyPartsEnum("Head", 0xa);
void reset_camera() {
// bool rMousePressFlg = false;
void SetCameraMode(CameraMode mode) {
cameraMode = mode;
}
CameraMode GetCameraMode() {
return cameraMode;
}
void SetFirstPersonRoll(FirstPersonRoll mode) {
firstPersonRoll = mode;
}
FirstPersonRoll GetFirstPersonRoll() {
return firstPersonRoll;
}
void reset_camera() {
followCharaIndex = 0;
firstPersonPosOffset = {0, 0.064f, 0.000f}; // f3: 0.008f
followPosOffset = {0, 0, 1.5};
followLookAtOffset = {0, 0};
baseCamera.reset();
}
void camera_forward() { // 向前
baseCamera.set_lon_move(0, LonMoveHState::LonMoveForward);
switch (cameraMode) {
case CameraMode::FREE: {
baseCamera.set_lon_move(0, LonMoveHState::LonMoveForward);
} break;
case CameraMode::FIRST_PERSON: {
firstPersonPosOffset.z += offsetMoveStep;
} break;
case CameraMode::FOLLOW: {
followPosOffset.z -= offsetMoveStep;
}
}
}
void camera_back() { // 后退
baseCamera.set_lon_move(180, LonMoveHState::LonMoveBack);
switch (cameraMode) {
case CameraMode::FREE: {
baseCamera.set_lon_move(180, LonMoveHState::LonMoveBack);
} break;
case CameraMode::FIRST_PERSON: {
firstPersonPosOffset.z -= offsetMoveStep;
} break;
case CameraMode::FOLLOW: {
followPosOffset.z += offsetMoveStep;
}
}
}
void camera_left() { // 向左
baseCamera.set_lon_move(90);
switch (cameraMode) {
case CameraMode::FREE: {
baseCamera.set_lon_move(90);
} break;
case CameraMode::FOLLOW: {
// followPosOffset.x += 0.8;
followLookAtOffset.x += offsetMoveStep;
}
default:
break;
}
}
void camera_right() { // 向右
baseCamera.set_lon_move(-90);
switch (cameraMode) {
case CameraMode::FREE: {
baseCamera.set_lon_move(-90);
} break;
case CameraMode::FOLLOW: {
// followPosOffset.x -= 0.8;
followLookAtOffset.x -= offsetMoveStep;
}
default:
break;
}
}
void camera_down() { // 向下
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
switch (cameraMode) {
case CameraMode::FREE: {
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
baseCamera.pos.y -= preStep;
baseCamera.lookAt.y -= preStep;
std::this_thread::sleep_for(std::chrono::milliseconds(BaseCamera::sleepTime));
}
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
baseCamera.pos.y -= preStep;
baseCamera.lookAt.y -= preStep;
std::this_thread::sleep_for(std::chrono::milliseconds(BaseCamera::sleepTime));
}
} break;
case CameraMode::FIRST_PERSON: {
firstPersonPosOffset.y -= offsetMoveStep;
} break;
case CameraMode::FOLLOW: {
// followPosOffset.y -= offsetMoveStep;
followLookAtOffset.y -= offsetMoveStep;
}
}
}
void camera_up() { // 向上
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
baseCamera.pos.y += preStep;
baseCamera.lookAt.y += preStep;
std::this_thread::sleep_for(std::chrono::milliseconds(BaseCamera::sleepTime));
}
void camera_up() { // 向上
switch (cameraMode) {
case CameraMode::FREE: {
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
baseCamera.pos.y += preStep;
baseCamera.lookAt.y += preStep;
std::this_thread::sleep_for(std::chrono::milliseconds(BaseCamera::sleepTime));
}
} break;
case CameraMode::FIRST_PERSON: {
firstPersonPosOffset.y += offsetMoveStep;
} break;
case CameraMode::FOLLOW: {
// followPosOffset.y += offsetMoveStep;
followLookAtOffset.y += offsetMoveStep;
}
}
}
void cameraLookat_up(float mAngel, bool mouse = false) {
baseCamera.horizontalAngle += mAngel;
@ -89,6 +189,173 @@ namespace GKCamera {
baseCamera.fov += value;
}
void SwitchCameraMode() {
switch (cameraMode) {
case CameraMode::FREE: {
cameraMode = CameraMode::FOLLOW;
GakumasLocal::Log::Info("CameraMode: FOLLOW");
} break;
case CameraMode::FOLLOW: {
cameraMode = CameraMode::FIRST_PERSON;
GakumasLocal::Log::Info("CameraMode: FIRST_PERSON");
} break;
case CameraMode::FIRST_PERSON: {
cameraMode = CameraMode::FREE;
GakumasLocal::Log::Info("CameraMode: FREE");
} break;
}
}
void SwitchCameraSubMode() {
switch (cameraMode) {
case CameraMode::FIRST_PERSON: {
if (firstPersonRoll == FirstPersonRoll::ENABLE_ROLL) {
firstPersonRoll = FirstPersonRoll::DISABLE_ROLL;
GakumasLocal::Log::Info("FirstPersonRoll: DISABLE_ROLL");
}
else {
firstPersonRoll = FirstPersonRoll::ENABLE_ROLL;
GakumasLocal::Log::Info("FirstPersonRoll: ENABLE_ROLL");
}
} break;
case CameraMode::FOLLOW: {
if (followModeY == FollowModeY::APPLY_Y) {
followModeY = FollowModeY::SMOOTH_Y;
GakumasLocal::Log::Info("FollowModeY: SMOOTH_Y");
}
else {
followModeY = FollowModeY::APPLY_Y;
GakumasLocal::Log::Info("FollowModeY: APPLY_Y");
}
} break;
default: break;
}
}
void OnLeftDown() {
if (cameraMode == CameraMode::FREE) return;
if (followCharaIndex >= 1) {
followCharaIndex--;
}
}
void OnRightDown() {
if (cameraMode == CameraMode::FREE) return;
followCharaIndex++;
}
void OnUpDown() {
if (cameraMode == CameraMode::FOLLOW) {
const auto currPart = bodyPartsEnum.Last();
GakumasLocal::Log::InfoFmt("Look at: %s (0x%x)", currPart.first.c_str(), currPart.second);
}
}
void OnDownDown() {
if (cameraMode == CameraMode::FOLLOW) {
const auto currPart = bodyPartsEnum.Next();
GakumasLocal::Log::InfoFmt("Look at: %s (0x%x)", currPart.first.c_str(), currPart.second);
}
}
void ChangeLiveFollowCameraOffsetY(const float value) {
if (cameraMode == CameraMode::FOLLOW) {
followPosOffset.y += value;
}
}
void ChangeLiveFollowCameraOffsetX(const float value) {
if (cameraMode == CameraMode::FOLLOW) {
followPosOffset.x += value;
}
}
UnityResolve::UnityType::Vector3 CalcPositionFromLookAt(const UnityResolve::UnityType::Vector3& target,
const UnityResolve::UnityType::Vector3& offset) {
// offset: z 远近, y 高低, x角度
const float angleX = offset.x;
const float distanceZ = offset.z;
const float angleRad = angleX * (M_PI / 180.0f);
const float newX = target.x + distanceZ * std::sin(angleRad);
const float newZ = target.z + distanceZ * std::cos(angleRad);
const float newY = target.y + offset.y;
return UnityResolve::UnityType::Vector3(newX, newY, newZ);
}
float CheckNewY(const UnityResolve::UnityType::Vector3& targetPos, const bool recordY) {
static GakumasLocal::Misc::FixedSizeQueue<float> recordsY(60);
const auto currentY = targetPos.y;
static auto lastRetY = currentY;
if (followModeY == FollowModeY::APPLY_Y) {
lastRetY = currentY;
return currentY;
}
const auto currentAvg = recordsY.Average();
// GakumasLocal::Log::DebugFmt("currentY: %f, currentAvg: %f, diff: %f", currentY, currentAvg, abs(currentY - currentAvg));
if (recordY) {
recordsY.Push(currentY);
}
if (abs(currentY - currentAvg) < 0.02) {
return lastRetY;
}
const auto retAvg = recordsY.Average();
lastRetY = lastRetY + (retAvg - lastRetY) / 8;
return lastRetY;
}
UnityResolve::UnityType::Vector3 CalcFollowModeLookAt(const UnityResolve::UnityType::Vector3& targetPos,
const UnityResolve::UnityType::Vector3& posOffset,
const bool recordY) {
const float angleX = posOffset.x;
const float angleRad = (angleX + (followPosOffset.z >= 0 ? 90.0f : -90.0f)) * (M_PI / 180.0f);
UnityResolve::UnityType::Vector3 newTargetPos = targetPos;
newTargetPos.y = CheckNewY(targetPos, recordY);
const float offsetX = followLookAtOffset.x * sin(angleRad);
const float offsetZ = followLookAtOffset.x * cos(angleRad);
newTargetPos.x += offsetX;
newTargetPos.z += offsetZ;
newTargetPos.y += followLookAtOffset.y;
return newTargetPos;
}
UnityResolve::UnityType::Vector3 CalcFirstPersonPosition(const UnityResolve::UnityType::Vector3& position,
const UnityResolve::UnityType::Vector3& forward,
const UnityResolve::UnityType::Vector3& offset) {
using Vector3 = UnityResolve::UnityType::Vector3;
// 计算角色的右方向
Vector3 up(0, 1, 0); // Y轴方向
Vector3 right = forward.cross(up).Normalize();
Vector3 fwd = forward;
Vector3 pos = position;
// 计算角色的左方向
Vector3 left = right * -1.0f;
// 计算最终位置
Vector3 backwardOffset = fwd * -offset.z;
Vector3 leftOffset = left * offset.x;
Vector3 finalPosition = pos + backwardOffset + leftOffset;
finalPosition.y += offset.y;
return finalPosition;
}
struct CameraMoveState {
bool w = false;
bool s = false;
@ -137,10 +404,10 @@ namespace GKCamera {
if (cameraMoveState.right) cameraLookat_right(moveAngel);
if (cameraMoveState.q) changeCameraFOV(0.5f);
if (cameraMoveState.e) changeCameraFOV(-0.5f);
// if (cameraMoveState.i) changeLiveFollowCameraOffsetY(moveStep / 3);
// if (cameraMoveState.k) changeLiveFollowCameraOffsetY(-moveStep / 3);
// if (cameraMoveState.j) changeLiveFollowCameraOffsetX(moveStep * 10);
// if (cameraMoveState.l) changeLiveFollowCameraOffsetX(-moveStep * 10);
if (cameraMoveState.i) ChangeLiveFollowCameraOffsetY(offsetMoveStep);
if (cameraMoveState.k) ChangeLiveFollowCameraOffsetY(-offsetMoveStep);
if (cameraMoveState.j) ChangeLiveFollowCameraOffsetX(0.8);
if (cameraMoveState.l) ChangeLiveFollowCameraOffsetX(-0.8);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}).detach();
@ -161,14 +428,30 @@ namespace GKCamera {
cameraMoveState.ctrl = message == WM_KEYDOWN; break;
case KEY_SPACE:
cameraMoveState.space = message == WM_KEYDOWN; break;
case KEY_UP:
cameraMoveState.up = message == WM_KEYDOWN; break;
case KEY_DOWN:
cameraMoveState.down = message == WM_KEYDOWN; break;
case KEY_LEFT:
cameraMoveState.left = message == WM_KEYDOWN; break;
case KEY_RIGHT:
cameraMoveState.right = message == WM_KEYDOWN; break;
case KEY_UP: {
if (message == WM_KEYDOWN) {
OnUpDown();
}
cameraMoveState.up = message == WM_KEYDOWN;
} break;
case KEY_DOWN: {
if (message == WM_KEYDOWN) {
OnDownDown();
}
cameraMoveState.down = message == WM_KEYDOWN;
} break;
case KEY_LEFT: {
if (message == WM_KEYDOWN) {
OnLeftDown();
}
cameraMoveState.left = message == WM_KEYDOWN;
} break;
case KEY_RIGHT: {
if (message == WM_KEYDOWN) {
OnRightDown();
}
cameraMoveState.right = message == WM_KEYDOWN;
} break;
case KEY_Q:
cameraMoveState.q = message == WM_KEYDOWN; break;
case KEY_E:
@ -183,7 +466,9 @@ namespace GKCamera {
cameraMoveState.l = message == WM_KEYDOWN; break;
case KEY_R: {
if (message == WM_KEYDOWN) reset_camera();
}; break;
} break;
case KEY_F: if (message == WM_KEYDOWN) SwitchCameraMode(); break;
case KEY_V: if (message == WM_KEYDOWN) SwitchCameraSubMode(); break;
default: break;
}
}

View File

@ -2,7 +2,43 @@
#include "baseCamera.hpp"
namespace GKCamera {
extern BaseCamera::Camera baseCamera;
enum class CameraMode {
FREE,
FIRST_PERSON,
FOLLOW
};
enum class FirstPersonRoll {
ENABLE_ROLL,
DISABLE_ROLL
};
enum class FollowModeY {
APPLY_Y,
SMOOTH_Y
};
void SetCameraMode(CameraMode mode);
CameraMode GetCameraMode();
void SetFirstPersonRoll(FirstPersonRoll mode);
FirstPersonRoll GetFirstPersonRoll();
extern BaseCamera::Camera baseCamera;
extern UnityResolve::UnityType::Vector3 firstPersonPosOffset;
extern UnityResolve::UnityType::Vector3 followPosOffset;
extern int followCharaIndex;
extern GakumasLocal::Misc::CSEnum bodyPartsEnum;
UnityResolve::UnityType::Vector3 CalcPositionFromLookAt(const UnityResolve::UnityType::Vector3& target,
const UnityResolve::UnityType::Vector3& offset);
UnityResolve::UnityType::Vector3 CalcFirstPersonPosition(const UnityResolve::UnityType::Vector3& position,
const UnityResolve::UnityType::Vector3& forward,
const UnityResolve::UnityType::Vector3& offset);
UnityResolve::UnityType::Vector3 CalcFollowModeLookAt(const UnityResolve::UnityType::Vector3& targetPos,
const UnityResolve::UnityType::Vector3& posOffset,
const bool recordY = false);
void on_cam_rawinput_keyboard(int message, int key);
void initCameraSettings();

View File

@ -45,7 +45,7 @@
#include "xdl.h"
#include "../../GakumasLocalify/Log.h"
#include "../../GakumasLocalify/Misc.h"
#include "../../GakumasLocalify/Misc.hpp"
class UnityResolve final {
public:
@ -906,6 +906,14 @@ public:
}
auto operator ==(const Vector3 x) const -> bool { return this->x == x.x && this->y == x.y && this->z == x.z; }
Vector3 cross(const Vector3& other) const {
return Vector3(
y * other.z - z * other.y,
z * other.x - x * other.z,
x * other.y - y * other.x
);
}
};
struct Vector2 {
@ -1580,6 +1588,14 @@ public:
if (!method) method = Get("UnityEngine.CoreModule.dll")->Get("Object")->Get<Method>("Destroy", { "*" });
if (method) return method->Invoke<void>(original);
}
static auto op_Implicit(UnityObject* exists) -> bool {
if (!exists) return false;
static Method* method;
if (!method) method = Get("UnityEngine.CoreModule.dll")->Get("Object", "UnityEngine")->Get<Method>("op_Implicit");
if (method) return method->Invoke<bool>(exists);
return false;
}
};
struct Component : UnityObject {
@ -1816,6 +1832,20 @@ public:
if (!method) method = Get("UnityEngine.CoreModule.dll")->Get("Camera")->Get<Method>("get_orthographicSize");
return method->Invoke<float>(this);
}
void SetNearClipPlane(float value) {
if (!this) return;
static Method* method;
if (!method) method = Get("UnityEngine.CoreModule.dll")->Get("Camera")->Get<Method>("set_nearClipPlane");
return method->Invoke<void>(this, value);
}
float GetNearClipPlane() {
if (!this) return -1;
static Method* method;
if (!method) method = Get("UnityEngine.CoreModule.dll")->Get("Camera")->Get<Method>("get_nearClipPlane");
return method->Invoke<float>(this);
}
};
struct Transform : Component {