增加 第一人称镜头、视角锁定镜头 (#9)
* 增加 第一人称镜头、视角锁定镜头 * Follow 摄像机增加平滑 Y 轴模式; 支持 LookAt 偏移
This commit is contained in:
parent
c5fd80daa2
commit
6a7717a45e
@ -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,11 +142,70 @@ 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) {
|
||||
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;
|
||||
@ -149,32 +213,19 @@ namespace GakumasLocal::HookMain {
|
||||
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"));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "Log.h"
|
||||
#include <android/log.h>
|
||||
#include <Misc.h>
|
||||
#include <Misc.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
76
app/src/main/cpp/GakumasLocalify/Misc.hpp
Normal file
76
app/src/main/cpp/GakumasLocalify/Misc.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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,26 +31,102 @@
|
||||
|
||||
namespace GKCamera {
|
||||
BaseCamera::Camera baseCamera{};
|
||||
CameraMode cameraMode = CameraMode::FREE;
|
||||
FirstPersonRoll firstPersonRoll = FirstPersonRoll::ENABLE_ROLL;
|
||||
FollowModeY followModeY = FollowModeY::SMOOTH_Y;
|
||||
|
||||
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);
|
||||
|
||||
// bool rMousePressFlg = false;
|
||||
|
||||
void SetCameraMode(CameraMode mode) {
|
||||
cameraMode = mode;
|
||||
}
|
||||
|
||||
CameraMode GetCameraMode() {
|
||||
return cameraMode;
|
||||
}
|
||||
|
||||
void SetFirstPersonRoll(FirstPersonRoll mode) {
|
||||
firstPersonRoll = mode;
|
||||
}
|
||||
|
||||
FirstPersonRoll GetFirstPersonRoll() {
|
||||
return firstPersonRoll;
|
||||
}
|
||||
|
||||
bool rMousePressFlg = false;
|
||||
|
||||
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() { // 向前
|
||||
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() { // 后退
|
||||
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() { // 向左
|
||||
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() { // 向右
|
||||
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() { // 向下
|
||||
switch (cameraMode) {
|
||||
case CameraMode::FREE: {
|
||||
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
|
||||
|
||||
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
|
||||
@ -55,8 +134,20 @@ namespace GKCamera {
|
||||
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() { // 向上
|
||||
switch (cameraMode) {
|
||||
case CameraMode::FREE: {
|
||||
float preStep = BaseCamera::moveStep / BaseCamera::smoothLevel;
|
||||
|
||||
for (int i = 0; i < BaseCamera::smoothLevel; i++) {
|
||||
@ -64,6 +155,15 @@ namespace GKCamera {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,43 @@
|
||||
#include "baseCamera.hpp"
|
||||
|
||||
namespace GKCamera {
|
||||
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();
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user