├── fxmanifest.lua ├── README.md ├── nui ├── style.css ├── index.html └── script.js └── client └── camera.lua /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version "cerulean" 2 | game "gta5" 3 | lua54 "yes" 4 | 5 | version "1.0.0" 6 | name "cuchi_guide" 7 | author "Cu-chi" 8 | description "Camera editor" 9 | 10 | client_scripts { 11 | "client/camera.lua" 12 | } 13 | 14 | ui_page "nui/index.html" 15 | 16 | files { 17 | "nui/*" 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cuchi_cameraEditor 2 | Easily create a camera with settings 3 | ![screenshot](https://github.com/Cu-chi/cuchi_cameraEditor/assets/110144918/42547958-6a0c-48a8-8fba-e3075f8ad834) 4 | 5 | # Tutorial 6 | Use the `createCam` command to start. Then copy the camera settings and use it in your code or wherever you want. 7 | -------------------------------------------------------------------------------- /nui/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | h1 { 7 | margin: 10px; 8 | } 9 | 10 | button { 11 | outline: none; 12 | font-family: Arial, Helvetica, sans-serif; 13 | color: #0D0B09; 14 | background-color: #cdc3b1; 15 | border-radius: 4px; 16 | font-size: medium; 17 | } 18 | 19 | button:active { 20 | opacity: 75%; 21 | } 22 | 23 | #blured { 24 | background-color: #cdc3b1; 25 | width: 100%; 26 | height: 100%; 27 | position: absolute; 28 | filter: blur(120px); 29 | z-index: -1; 30 | } 31 | 32 | .indicator { 33 | outline: none; 34 | background-color: #0D0B09; 35 | border: solid 2px #cdc3b1; 36 | border-radius: 4px; 37 | color: #F1EFEC; 38 | font-family: Arial, Helvetica, sans-serif; 39 | font-size: medium; 40 | width: 3em; 41 | text-align: center; 42 | } 43 | 44 | #container { 45 | position: absolute; 46 | display: none; 47 | flex-direction: column; 48 | align-items: center; 49 | background-color: #0D0B09; 50 | border-radius: 8px; 51 | padding: 10px; 52 | margin: 40px; 53 | gap: 5px; 54 | font-family: Arial, Helvetica, sans-serif; 55 | color: #F1EFEC; 56 | } 57 | 58 | .category { 59 | display: flex; 60 | gap: 10px; 61 | justify-content: center; 62 | align-items: center; 63 | } 64 | -------------------------------------------------------------------------------- /nui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Cuchi Guide Camera 8 | 9 | 10 |
11 |
12 |

Camera Editor

13 |
14 |

Rotate X

15 | 16 | 17 | 18 |

0

19 |
20 |
21 |

Rotate Y

22 | 23 | 24 | 25 |

0

26 |
27 |
28 |

Rotate Z

29 | 30 | 31 | 32 |

0

33 |
34 |
35 |
36 |

Position X

37 | 38 | 39 | 40 |

0

41 |
42 |
43 |

Position Y

44 | 45 | 46 | 47 |

0

48 |
49 |
50 |

Position Z

51 | 52 | 53 | 54 |

0

55 |
56 |
57 |
58 |

FOV

59 | 60 | 61 | 62 |

0

63 |
64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /client/camera.lua: -------------------------------------------------------------------------------- 1 | local creatingCamera = false 2 | local camera 3 | local rotation 4 | local cameraPosition 5 | local FOV = 45.0 6 | 7 | function CreateCamera(coords) 8 | if not creatingCamera then 9 | creatingCamera = true 10 | 11 | cameraPosition = { 12 | x = tonumber(string.format("%.f", coords.x)) + 0.0, 13 | y = tonumber(string.format("%.f", coords.y)) + 0.0, 14 | z = tonumber(string.format("%.f", coords.z)) + 0.0 15 | } 16 | 17 | rotation = { 18 | x = 0.0, 19 | y = 0.0, 20 | z = 0.0 21 | } 22 | 23 | SendNUIMessage({ 24 | show = true, 25 | pos = cameraPosition, 26 | rot = rotation 27 | }) 28 | 29 | SetNuiFocus(true, true) 30 | 31 | while creatingCamera do 32 | camera = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", cameraPosition.x, cameraPosition.y, cameraPosition.z, rotation.x, rotation.y, rotation.z, FOV, true, 2) 33 | RenderScriptCams(true, false, 0, false, false) 34 | SetFocusPosAndVel(cameraPosition.x, cameraPosition.y, cameraPosition.z, 0.0, 0.0, 0.0) 35 | 36 | Wait(0) 37 | 38 | DestroyCam(camera, true) 39 | end 40 | else 41 | creatingCamera = false 42 | DestroyCam(camera, true) 43 | RenderScriptCams(false, false, 0, false, false) 44 | SetFocusEntity(PlayerPedId()) 45 | SendNUIMessage({ show = false, }) 46 | SetNuiFocus(false, false) 47 | end 48 | end 49 | 50 | RegisterCommand("createCam", function() 51 | local playerPed = PlayerPedId() 52 | local playerPosition = GetEntityCoords(playerPed) 53 | CreateCamera(playerPosition) 54 | end, true) 55 | 56 | RegisterNUICallback("rotation", function(body, resultCallback) 57 | if body.axis == "x" then 58 | rotation.x += body.factor*1 59 | elseif body.axis == "y" then 60 | rotation.y += body.factor*1 61 | elseif body.axis == "z" then 62 | rotation.z += body.factor*1 63 | end 64 | resultCallback("ok") 65 | end) 66 | 67 | RegisterNUICallback("setRotation", function(body, resultCallback) 68 | local value = (tonumber(body.value) or 0.0) + 0.0 69 | if body.axis == "x" then 70 | rotation.x = value 71 | elseif body.axis == "y" then 72 | rotation.y = value 73 | elseif body.axis == "z" then 74 | rotation.z = value 75 | end 76 | resultCallback("ok") 77 | end) 78 | 79 | RegisterNUICallback("position", function(body, resultCallback) 80 | cameraPosition[body.axis] += body.factor*1 81 | resultCallback("ok") 82 | end) 83 | 84 | RegisterNUICallback("setPosition", function(body, resultCallback) 85 | local value = tonumber(body.value) or 0.0 86 | cameraPosition[body.axis] = value + 0.0 87 | resultCallback("ok") 88 | end) 89 | 90 | RegisterNUICallback("FOV", function(body, resultCallback) 91 | FOV += body.factor*1 92 | if FOV < 0 then 93 | FOV = 0.0 94 | elseif FOV > 130 then 95 | FOV = 130.0 96 | end 97 | resultCallback("ok") 98 | end) 99 | 100 | RegisterNUICallback("resetFOV", function(_, resultCallback) 101 | FOV = 45.0 102 | resultCallback("ok") 103 | end) 104 | 105 | RegisterNUICallback("close", function(_, resultCallback) 106 | creatingCamera = false 107 | DestroyCam(camera, true) 108 | RenderScriptCams(false, false, 0, false, false) 109 | 110 | SendNUIMessage({ show = false }) 111 | SetNuiFocus(false, false) 112 | SetFocusEntity(PlayerPedId()) 113 | 114 | resultCallback("ok") 115 | end) 116 | 117 | AddEventHandler("onResourceStop", function(resource) 118 | if resource == GetCurrentResourceName() then 119 | SetFocusEntity(PlayerPedId()) 120 | end 121 | end) 122 | -------------------------------------------------------------------------------- /nui/script.js: -------------------------------------------------------------------------------- 1 | const resName = GetParentResourceName(); 2 | let rotations = { x: 0, y: 0, z: 0 }; 3 | let defaultPositions = { x: 0, y: 0, z: 0 }; 4 | let positions = { x: 0, y: 0, z: 0 }; 5 | let FOV = 45; 6 | let fovInterval; 7 | 8 | window.addEventListener("message", (event) => { 9 | let data = event.data; 10 | let camCreatorElem = document.getElementById("container"); 11 | if (data.show) { 12 | camCreatorElem.style.display = "flex"; 13 | rotations = { x: 0, y: 0, z: 0 }; 14 | defaultPositions = data.pos; 15 | positions = { ...defaultPositions }; 16 | for (val of ["x", "y", "z"]) { 17 | document.getElementById("rot-"+val).innerText = rotations[val]; 18 | document.getElementById("pos-"+val).innerText = positions[val]; 19 | } 20 | 21 | document.getElementById("fov").innerText = FOV; 22 | } 23 | else { 24 | camCreatorElem.style.display = "none"; 25 | } 26 | }); 27 | 28 | window.addEventListener("keyup", (e) => { 29 | if (e.key === "Escape" || e.key === "Backspace") { 30 | fetch("https://"+resName+"/close", { 31 | method: "POST", 32 | body: null 33 | }); 34 | } 35 | }) 36 | 37 | for (val of ["x", "y", "z"]) { 38 | let value = val 39 | let interval; 40 | 41 | let positiveElem = document.getElementById("p-rot-"+value) 42 | positiveElem.onmousedown = () => { 43 | interval = setInterval(function() { 44 | fetch("https://"+resName+"/rotation", { 45 | method: "POST", 46 | body: JSON.stringify({ 47 | axis: value, 48 | factor: 1 49 | }) 50 | }); 51 | rotations[value]++; 52 | if (rotations[value] % 360 === 0) rotations[value] = 0; 53 | document.getElementById("rot-"+value).innerText = rotations[value]; 54 | }, 25); 55 | }; 56 | 57 | positiveElem.onmouseup = () => clearInterval(interval); 58 | positiveElem.onmouseleave = () => clearInterval(interval); 59 | 60 | let negativeElem = document.getElementById("n-rot-"+value) 61 | negativeElem.onmousedown = () => { 62 | interval = setInterval(function() { 63 | fetch("https://"+resName+"/rotation", { 64 | method: "POST", 65 | body: JSON.stringify({ 66 | axis: value, 67 | factor: -1 68 | }) 69 | }); 70 | rotations[value]--; 71 | if (rotations[value] % 360 === 0) rotations[value] = 0; 72 | document.getElementById("rot-"+value).innerText = rotations[value]; 73 | }, 25); 74 | }; 75 | 76 | negativeElem.onmouseup = () => clearInterval(interval); 77 | negativeElem.onmouseleave = () => clearInterval(interval); 78 | 79 | document.getElementById("r-rot-"+value).onclick = () => { 80 | rotations[value] = 0; 81 | document.getElementById("rot-"+value).innerText = rotations[value]; 82 | 83 | fetch("https://"+resName+"/setRotation", { 84 | method: "POST", 85 | body: JSON.stringify({ 86 | axis: value, 87 | value: 0 88 | }) 89 | }); 90 | }; 91 | 92 | positiveElem = document.getElementById("p-pos-"+value) 93 | positiveElem.onmousedown = () => { 94 | interval = setInterval(function() { 95 | fetch("https://"+resName+"/position", { 96 | method: "POST", 97 | body: JSON.stringify({ 98 | axis: value, 99 | factor: 1 100 | }) 101 | }); 102 | positions[value]++; 103 | document.getElementById("pos-"+value).innerText = positions[value]; 104 | }, 25); 105 | }; 106 | 107 | positiveElem.onmouseup = () => clearInterval(interval); 108 | positiveElem.onmouseleave = () => clearInterval(interval); 109 | 110 | negativeElem = document.getElementById("n-pos-"+value); 111 | negativeElem.onmousedown = () => { 112 | interval = setInterval(function() { 113 | fetch("https://"+resName+"/position", { 114 | method: "POST", 115 | body: JSON.stringify({ 116 | axis: value, 117 | factor: -1 118 | }) 119 | }); 120 | positions[value]--; 121 | document.getElementById("pos-"+value).innerText = positions[value]; 122 | }, 25); 123 | }; 124 | 125 | negativeElem.onmouseup = () => clearInterval(interval); 126 | negativeElem.onmouseleave = () => clearInterval(interval); 127 | 128 | document.getElementById("r-pos-"+value).onclick = () => { 129 | positions[value] = defaultPositions[value]; 130 | document.getElementById("pos-"+value).innerText = positions[value] 131 | 132 | fetch("https://"+resName+"/setPosition", { 133 | method: "POST", 134 | body: JSON.stringify({ 135 | axis: value, 136 | value: positions[value] 137 | }) 138 | }); 139 | }; 140 | } 141 | 142 | FOVPositiveElem = document.getElementById("p-fov"); 143 | FOVPositiveElem.onmousedown = () => { 144 | fovInterval = setInterval(function() { 145 | fetch("https://"+resName+"/FOV", { 146 | method: "POST", 147 | body: JSON.stringify({ 148 | factor: 1 149 | }) 150 | }); 151 | FOV++; 152 | if (FOV > 130) FOV = 130; 153 | document.getElementById("fov").innerText = FOV; 154 | }, 25); 155 | }; 156 | 157 | FOVPositiveElem.onmouseup = () => clearInterval(fovInterval); 158 | FOVPositiveElem.onmouseleave = () => clearInterval(fovInterval); 159 | 160 | FOVNegativeElem = document.getElementById("n-fov"); 161 | FOVNegativeElem.onmousedown = () => { 162 | fovInterval = setInterval(function() { 163 | fetch("https://"+resName+"/FOV", { 164 | method: "POST", 165 | body: JSON.stringify({ 166 | factor: -1 167 | }) 168 | }); 169 | FOV--; 170 | if (FOV < 0) FOV = 0; 171 | document.getElementById("fov").innerText = FOV; 172 | }, 25); 173 | }; 174 | 175 | FOVNegativeElem.onmouseup = () => clearInterval(fovInterval); 176 | FOVNegativeElem.onmouseleave = () => clearInterval(fovInterval); 177 | 178 | document.getElementById("r-fov").onclick = () => { 179 | document.getElementById("fov").innerText = 45; 180 | FOV = 45; 181 | fetch("https://"+resName+"/resetFOV", { 182 | method: "POST", 183 | body: null 184 | }); 185 | }; 186 | 187 | const copyToClipboard = (text) => { 188 | const element = document.createElement("textarea"); 189 | element.value = text; 190 | document.body.appendChild(element); 191 | element.select(); 192 | document.execCommand("copy"); 193 | document.body.removeChild(element); 194 | }; 195 | 196 | document.getElementById("copy").onclick = () => { 197 | copyToClipboard(`CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", ${positions.x}.0, ${positions.y}.0, ${positions.z}.0, ${rotations.x}.0, ${rotations.y}.0, ${rotations.z}.0, ${FOV}.0, true, 2)`); 198 | }; 199 | --------------------------------------------------------------------------------