[closed] Portfolio | Modeler, Programmer

Icon#2_Transparent_490px

:wave: ,

I am well versed in multiple aspects of game developments through many small solo projects.

My field of expertise:

  • 3D Assets; 6/10
    • Blender being the primary software I use, alongside with Substance Painter.
    • Creativity is not my forte so modelling isn’t really what I can do without any creative directive.
    • Experience: 3+ years.
  • Programming; 9.5/10
    • Fluent with Lua, Python, Javascript.
    • Extremely well with both back-end and front-end stuff, i.e data handling, databases etc.
    • Have experiences on different programming paradigms with different concepts such as composition and inheritance. (Mostly OOP)
    • Created basic full stack web applications with NodeJS.
    • Experience: 4+ years.
  • Image manipulation; 7/10
    • Very fluent with Paint.net.
    • Been using it to create simple user interfaces.
    • Lack the ability to draw though.
  • Animation; 4/10
    • Have the ability to set up rigs within Blender, with knowledge on armatures and weight painting.
    • Rather good understanding of the fundamentals.
    • Such as keyframing, frames interpolation etc.
  • Roblox Studio; 9/10
    • Been on it for almost 3 years now.
    • Vast knowledge of this game engine.

Models:

Low poly styled:

  • food objects

  • low poly styled stage light

  • staff

  • cannongun


  • pumps taking the shape of traditional Japan buildings

  • self ordering kiosk

  • classic pine tree

  • flamethrower design A

  • flamethrower design B

  • japanese-styled lantern

High poly; Renders:

  • cola can render; can’s texture was obtained from the internet, with materials done within Blender

  • headset render, modelled and rendered all in Blender

  • medieval helmet


  • trash can with car-like wheels :skull:


  • renders of the Lamy Safari Fountain Pen; final model had some variation in terms of physical shape

UGC Concepts:

  • portable charger to ensure your device won’t run out of juice

  • antenna powered by brain juices, no more bad connections

Codes:

What I am able to do:

  • Datastores; both the Roblox Datastore and external datastores interacted with the REST API such as Firebase by Google (with authentication workflows implemented such as OAuth2).
  • Tween service.
  • CFrames; highly experienced with this. I’ve had many projects that needed heavy CFrames to achieve the intended outcome.
  • Modulescripts for modular systems.
  • OOP, metatables and metamethods etc.
  • Interfaces. Maintaining good infrastructure of UI elements.
  • Raycasting, segmented raycsating (recursive).
  • Bodymovers. Only experience was creating spaceships.
  • UserInputService (UIS)
  • ContextActionService (CAS)

What I am unable to do:

  • Custom characters.

Model Showcase system

Made this as an effort to showcase my friend’s amazing models.
I’ve used,

  • tweenservice,
  • userinputservice
  • runservice
  • viewportframes
    to achieve it.

Featuring a free camera movement to view the models from all angles,

function setCamCFrame(cam, object, x, y, offset)
	local camAng = cam.CFrame
	local camAngX, camAngY, camAngZ = camAng:ToEulerAnglesYXZ()
	local angleX = math.deg(camAngX) -y
	if angleX > 65 then
		angleX = 65
	end
	if angleX < -65 then
		angleX = -65
	end
	camAng = CFrame.fromOrientation(math.rad(angleX), camAngY +math.rad(x), 0)
	cam.CFrame = (object.PrimaryPart.CFrame * camAng * CFrame.new(0, 0, offset))
end

Short Showcase:

Image from Gyazo


Search System:

Search system uses string.find(); nothing significant
Image from Gyazo


Game:

portfolio thing - Roblox


Mirroring GUI

Mirrors the GUI Frame along with its descendants and reflect it onto a new screen
Works for both axis
Nothing of any significance, just ought I left it here.

local frame = script.Parent:WaitForChild("Frame")

local function mirrorDescendants(frameParam, vertical)
    local newFrame = frameParam:Clone()
    newFrame.Parent = script.Parent
    if vertical then
        for _, children in pairs(newFrame:GetDescendants()) do
            if children:IsA("GuiObject") then
                local X, Y = 1 - children.AnchorPoint.X, 1 - children.AnchorPoint.Y
                children.AnchorPoint = Vector2.new(children.AnchorPoint.X, Y)
                local pos = children.Position
                children.Position = UDim2.new(pos.X.Scale, 0, 1 - pos.Y.Scale, 0)
            end
        end
    else
        for _, children in pairs(newFrame:GetDescendants()) do
            if children:IsA("GuiObject") then
                local X, Y = 1 - children.AnchorPoint.X, 1 - children.AnchorPoint.Y
                children.AnchorPoint = Vector2.new(X, children.AnchorPoint.Y)
                local pos = children.Position
                children.Position = UDim2.new(1- pos.X.Scale, 0, pos.Y.Scale, 0)
            end
        end
    end
    return newFrame
end
local mirroredFrame = mirrorDescendants(frame, true)
mirroredFrame.Position = UDim2.new(0.3, 0, 0.7, 0)
local mirroredFrame = mirrorDescendants(frame, false)
mirroredFrame.Position = UDim2.new(0.7, 0, 0.3, 0)

Horizontal and vertical mirroring


Studio Plugin

A plugin that shows the currently selected(or previously selected if no parts are selected) part’s size.

Image from Gyazo


It features a string formatting system that gives each strings a set amount of width
This prevents the individual values from shifting from their position without the need of 3 different textlabels.
Helps with readability
Image from Gyazo

local emptySpace = "        "
local function format(str)
	str = tostring(str)
    if string.len(str) < 10 then
        local emptySpaceNeeded = string.len(str) - 8
        local emptySpace = string.sub(emptySpace, emptySpaceNeeded)
        local newStringWithEmptySpace = str..emptySpace
        
        return newStringWithEmptySpace
    end
    
    return str
end

Code responsible for getting the selection and size.

local function GetSize(obj, gui)
	local x, y, z = obj.Size.X, obj.Size.Y, obj.Size.Z
	
	local list = {x, y, z}
	for i, v in ipairs(list) do
		v = round(v)
		list[i] = v
	end
	local str = finalFormat(list)
	
	gui = gui.TextLabel
	gui.Text = str
end

local function getObject(gui)
	local obj = Selection:Get()[#Selection:Get()]
	
	if not (obj:IsA('BasePart')) or obj == nil then
		return
	end
	GetSize(obj, gui)
	return obj
end
local event = nil
Selection.SelectionChanged:Connect(function()
	currentlySelect = getObject(gui)
	if currentlySelect ~= nil then
		if event ~= nil then event:Disconnect() end
		event = currentlySelect:GetPropertyChangedSignal('Size'):Connect(function()
			GetSize(currentlySelect, gui)
		end)
	end
end)

Generic Datastore

A functional datastore backend.
Roblox datastore’s wrapper?

Uses proxy tables as metatables to detect when a value changes.
__newindex metamethod tanking the whole feature of value change detection

local backend = {}
local ds = game:GetService("DataStoreService"):GetDataStore("testb")
local rs = game:GetService("ReplicatedStorage")

-- constants
local RETRYLIMIT = 5

-- default values for new players
local defaultData = {
	time_played = 0
}

-- to notify player if a value change occurred
local hookEvent = Instance.new("RemoteEvent", rs:WaitForChild("datastoreObjects"))
local hooks = {} -- store player instance if plr wants to be notified
local serverHooks = {} -- for server hook

local cache = {} -- read
local proxy = {} -- write; contents of proxy will never be written
-- proxy[key] = value will transfer towards cache table, cache[key] = value
-- to allow __newindex to trigger as luau and lua have different behaviours
local function transformDictKeysToString(dict)
	-- recursive function
	local re = {}
	for k, v in pairs(dict) do
		if type(k) == "number" then
			k =  tostring(k)
		end
		if type(v) == "table" then
			v = transformDictKeysToString(v)
		end
		re[k] = v
	end
	return re
end
setmetatable(proxy, {
	__newindex = function(a, plr, v)
	print('change detected')
	cache[plr] = v
	if v == nil then
		return
	end
	local send = transformDictKeysToString(v) -- to enable dictionaries to be sent through remotes/bindables
	if hooks[plr] then
		hookEvent:FireClient(plr, v)
	end
	for _, serverHookEvent in pairs(serverHooks) do
		print('firing')
		serverHookEvent:Fire(plr, v)
	end
end})

function backend.getData(plr)
	-- retrieves data
	local plrData = nil
	if cache[plr] ~= nil then
		plrData = cache[plr]
	else
		for i = 1, RETRYLIMIT do
			local s, e = pcall(function()
				plrData = ds:GetAsync(plr.UserId)
			end)
			if s then
				break
			elseif i == RETRYLIMIT then
				warn("could not call :GetAsync successfully, with error, "..tostring(e))
				return {false, e}
			end
		end
		if plrData == nil then
			plrData = defaultData
			for i = 1, RETRYLIMIT do
				local s, e = pcall(function()
					ds:SetAsync(plr.UserId, plrData)
				end)
				if s then
					break
				elseif i == RETRYLIMIT then
					warn("Could not call :SetAsync successfully, with error: "..tostring(e))
				end
			end
		end
		proxy[plr] = plrData
	end

	return {true, plrData}
end
function backend.pushData(plr)
	-- pushes cached data into datastore
	local plrData = backend.getData(plr)
	if not plrData[1] or plrData[2] == nil then
		-- SHOULD NOT HAPPEN AS GETASYNC WAS CALLED TO SET CACHE VALUE
		warn("Could not retrieve player data to push to datastore with error: "..tostring(plrData[2]))
		plrData = cache[plr] -- as an last ditch effort
		if plrData == nil then
			error("Could not push data to datastore as plrData is nil")
			return {false, "plrData is nil"}
		end
	end

	for i = 1, RETRYLIMIT do
		local s, e = pcall(function()
			ds:UpdateAsync(plr.UserId, function()
				return plrData[2]
			end)
		end)
		if s then
			break
		elseif i == RETRYLIMIT then
			error("Could not push data to datastore as :UpdateAsync could not be called with error: "..tostring(e))
			return {false, e}
		end
	end
	return {true}
end
function backend.setData(plr, newData)
	-- updates the data stored with newData
	proxy[plr] = newData
end
function backend.changeValueByKey(plr, k, v)
	if cache[plr] == nil then
		backend.getData(plr) -- init
	end
	local data = cache[plr]
	data[k] = v

	proxy[plr] = data
end
function backend.incrementValueByKey(plr, k, v)
	if cache[plr] == nil then
		backend.getData(plr) -- init
	end
	local data = cache[plr]
	data[k] += v

	proxy[plr] = data
end
function backend.getDefaultData()
	return defaultData
end
function backend.removeReferences(plr)
	-- called on PlayerRemoving event
	if cache[plr] == nil then
		-- player joined and leave immediately
	else
		backend.pushData(plr)
	end
	cache[plr] = nil
	hooks[plr] = false
end
function backend.getHook(plr)
	-- fires a remoteevent upon data changes
	hooks[plr] = true
	return hookEvent
end
function backend.dropHook(plr)
	hooks[plr] = false
	return true
end
function backend.newServerHook()
	local newHook = Instance.new("BindableEvent", script)
	newHook.Parent = rs:WaitForChild("datastoreObjects")

	table.insert(serverHooks, newHook)
	return newHook
end
return backend

On addition to this, another script is used as a proxy to interact with the module script.
I.e:

local plrs = game:GetService("Players")
local rs = game:GetService("ReplicatedStorage")

local remoteFunc = rs:WaitForChild("Remotes"):WaitForChild("PetData")
local serverFunc = script:WaitForChild("serverConnection")
local ds = require(script:WaitForChild("datastore"))

local tasks = {
	function(plr) -- get data
		return ds.getData(plr)
	end,
	function(plr) -- get hook
		return ds.getHook(plr)
	end,
	function(plr) -- drop hook
		return ds.dropHook(plr)
	end
}

local clientAllowed = {1, 2, 3}
remoteFunc.OnServerInvoke = function(plr, actioncode, ...)
	local passed = false
	for _, i in pairs(clientAllowed) do
		if actioncode == i then
			passed = true
			break
		end
	end
	if not passed then
		-- not permitted to be executed on the client
		return {false}
	end
	return tasks[actioncode](plr, ...)
end

serverFunc.OnInvoke = function(plr, actioncode, ...)
	return tasks[actioncode](plr, ...)
end

plrs.PlayerRemoving:Connect(function(plr)
	ds.removeReferences(plr) -- saves data too
end)

Furniture Place System

A basic furniture placement system that features rotations too.
Rather secure in terms of exploitability.
Uses OOP.

Steps

  • User selects what objects they want to place
CAS:BindAction('A', getObject, true, Enum.KeyCode.Q)
CAS:BindAction('B', getObject, true, Enum.KeyCode.E)
  • All of the placing is done on the client side, only replicated to the server when it passes checks.

  • Gets user’s mouse, clamp the value to ensure parts are within the canvas

local x = math.clamp(mouseX, self.gridZeroX, self.finalGridX)
local z = math.clamp(mouseZ, self.gridZeroZ, self.finalGridZ)
local ht = objHeight/2 + canvasPos.Y + canvasSize.Y/2
  • Gets the nearest grid’s position
local xA = math.floor((x - self.gridZeroX)/gridSpace +0.5)
local zA = math.floor((z - self.gridZeroZ)/gridSpace +0.5)

x = self.gridZeroX + gridSpace*xA
z = self.gridZeroZ + gridSpace*zA
  • Verify if object is not colliding with others
local function verifyPlace(objClass)
	local allowedtoplace = true
	local model = objClass.Object
	local modelPos = model.PrimaryPart.Position
	local modelSize = model.PrimaryPart.CFrame:VectorToWorldSpace(model.PrimaryPart.Size)
	for _, v in pairs(workspace.ignoredObjects:GetChildren()) do
		if v == model then continue end
		local vPos = v.PrimaryPart.Position
		local vSize = v.PrimaryPart.CFrame:VectorToWorldSpace(v.PrimaryPart.Size)
		local xFactor = math.max(vPos.X, modelPos.X) - math.min(vPos.X, modelPos.X)
		local xSize = math.abs(vSize.X)/2 + math.abs(modelSize.X)/2
		local zFactor = math.max(vPos.Z, modelPos.Z) - math.min(vPos.Z, modelPos.Z)
		local zSize = math.abs(vSize.Z)/2 + math.abs(modelSize.Z)/2
		if xFactor < xSize and zFactor < zSize then
			allowedtoplace = false
			break
		end
	end
	changetoRed(model, not allowedtoplace)
	return allowedtoplace	
end
function placement:PlaceVerify()
	local canPlace = verifyPlace(self)
	return canPlace
end

Constructed my own object of the class for every object.

This features,

  • Dynamic sizes,
  • Rotation,
  • Cancelling placement (just calling :Destroy() on the object),
  • Canvas lock, only allowing parts to be placed within the canvas,
  • Server sided checks, to ensure nothing is not where is shouldn’t be

Showcase:

Image from Gyazo


Stacking Blocks

Classic stacking game.
Its all purely parts, no CSG or Unions were involved.
Made possible with CFrames :sunglasses:.

Data saving along with leaderboard

  • Achieved it using OrderedDataStore
  • Refreshes approx. every 30minutes in game, intervals are influenced by the current DSS budget.

Showcase:

Stacking Game Showcase - YouTube


Discontinued work on it.


Purchase Listeners

Monitors PurchasePrompts using the PromptPurchaseFinished service.
Along with RemoteEvents and :SetCore() function.


Image from Gyazo


Loading Screen

A simple loading screen made using TweenService.
Main part was just choreographing everything.

Code:

-- disabling core guis
local startergui = game:GetService("StarterGui")
local UIS = game:GetService("UserInputService")
startergui:SetCoreGuiEnabled(Enum.CoreGuiType.All, false)
UIS.ModalEnabled = true
--

local contentprovider = game:GetService("ContentProvider")
local plr = game:GetService("Players").LocalPlayer
local ts = game:GetService("TweenService")

local fFrame = script.Parent:WaitForChild("first")
local sFrame = script.Parent:WaitForChild("second")

local touch = fFrame:WaitForChild("touch")
local initialMsg = fFrame:WaitForChild("init")
local promptMsg = fFrame:WaitForChild("prompt")
local fText = fFrame:WaitForChild("header")
local fImg = fFrame:WaitForChild("ImageLabel")

local sImg = sFrame:WaitForChild("ImageLabel")
local sBlinds = sImg:WaitForChild("blinds")
local sWhite = sFrame:WaitForChild("blank")

local LIMIT = 30 -- seconds
local function loading() -- yield control of current script until everything has loaded in
	local loaded = true
	local start = tick()

	contentprovider:PreloadAsync({fImg.Image, sImg.Image})
	while true do -- primitive check conditions for fully loaded
		if not fImg.IsLoaded then
			loaded = false
		elseif not sImg.IsLoaded then
			loaded = false
		elseif plr.Character == nil then
			loaded = false
		end

		if loaded or tick() - start > LIMIT then
			break
		end
		wait()
	end
	return false
end
local isLoading = true
coroutine.wrap(function()
	while isLoading do
		local suffix = ""
		for i = 1, 3 do
			suffix = suffix.."."
			initialMsg.Text = "Loading"..suffix
			wait(0.3)
		end
	end
end)()
local isLoading = loading() -- yield code
print("LOADING FINISHED")
initialMsg.Visible = false

local promptText_TI = TweenInfo.new(
	2,
	Enum.EasingStyle.Linear,
	Enum.EasingDirection.InOut,
	-1,
	true
)
local promptText_T = ts:Create(promptMsg, promptText_TI, {TextTransparency = 1})

local function tweenHandler(tweenObj, i, t)
	local tween = ts:Create(tweenObj, i, t)
	tween:Play()
	tween.Completed:Wait()
end
local fired = false -- simple debounce
touch.MouseButton1Click:Connect(function()
	-- disabling touch event
	if fired then
		return -- debounce
	end
	fired = true
	touch.Visible = false
	touch.Active = false

	-- hide prompt text
	promptMsg.Visible = false
	promptText_T:Cancel()

	-- fade in first background
	tweenHandler(fFrame, TweenInfo.new(1), {BackgroundColor3 = Color3.fromRGB(30, 30, 30)})

	-- fade in text
	local f_TI = TweenInfo.new(
		2,
		Enum.EasingStyle.Sine,
		Enum.EasingDirection.Out,
		0
	)
	fText.Visible = true -- show text
	fImg.Visible = true -- show image
	tweenHandler(fText, f_TI, {Position = UDim2.new(0.5, 0, 0.3, 0)})

	-- fade in image
	tweenHandler(fImg, f_TI, {Position = UDim2.new(0.5, 0, 0.5, 0)})

	wait(3)
	-- moving onto second frame
	local s_TI = TweenInfo.new(
		1.2,
		Enum.EasingStyle.Linear,
		Enum.EasingDirection.InOut,
		0
	)
	-- pull down second frame's container
	sFrame.Visible = true
	tweenHandler(sFrame, TweenInfo.new(2.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out, 0), {Position = UDim2.new(0.5, 0, 0, 0)})

	sImg.Visible = true -- show image icon

	-- pull down img's blind in second frame
	tweenHandler(sBlinds, f_TI, {Position = UDim2.new(0.5, 0, 1, 0)})
	wait(3.5)

	-- flashbang!!
	sWhite.Visible = true
	tweenHandler(sWhite, s_TI, {BackgroundTransparency = 0})

	-- hide first slide
	fFrame.Visible = false
	-- hide purple bg and img for second slide
	sFrame.BackgroundTransparency = 1
	sImg.Visible = false

	-- un-flashbang!!
	tweenHandler(sWhite, s_TI, {BackgroundTransparency = 1})

	wait(0.5)
	startergui:SetCoreGuiEnabled(Enum.CoreGuiType.All, true)
	UIS.ModalEnabled = false
end)

initialMsg.Visible = false -- hide loading msg

-- enable click event
touch.Visible = true
touch.Active = true

promptMsg.Visible = true
promptText_T:Play()

Showcase:


Event Class

A class that can send signals and fire functions between scripts.
Acts exactly like a BindableEvent in terms of how they work.
OOP at it’s finest B)

Code:

local signalHandler = {}

local signalClass = {}
signalClass.__index = signalClass

-- :Connect()'s and :Disconnect()'s events
local signalSiblingClass = {}
signalSiblingClass.__index = signalSiblingClass

function signalHandler.new()
	local newSignal = {}
	setmetatable(newSignal, signalClass)

	-- stores functions to be callled later on
	newSignal.fList = {}
	-- stores coroutine objects for :Wait()
	newSignal.tList = {}
	return newSignal
end

function signalClass:Connect(f)
	-- f being the transform function

	local newSiblingClass = {}
	setmetatable(newSiblingClass, signalSiblingClass)

	newSiblingClass.parent = self
	newSiblingClass.func = f
	
	self.fList[newSiblingClass] = true
	return newSiblingClass
end

function signalSiblingClass:Disconnect()
	-- self.parent is signalClass
	self.parent.fList[self] = nil
	setmetatable(self, nil)
	self.parent = nil
end

function signalClass:Wait()
	table.insert(self.tList, coroutine.running())

	return coroutine.yield()
end

function signalClass:Fire(...)
	for siblingclass in pairs(self.fList) do
		siblingclass.func(...)
	end
	-- fire threads
	for _, y in pairs(self.tList) do
		coroutine.resume(y, ...)
	end
	self.tList = {}
end

function signalClass:Destroy()
	for f in pairs(self.fList) do
		setmetatable(f, nil)
	end
	self.fList = {}
	setmetatable(self, nil)
end

return signalHandler

Demo usage code:

local signal = require(module)
local event = signal.new()
local connection = event:Connect(function(b)
	print(b)
end)
event:Fire("Hello world!")

wait(5)
connection:Disconnect()
event:Destroy()

Ferris Wheel

Demonstration of some CFrame math.


Showcase:


Games:

Geisha

Description: a game under the horror genre with the main theme on Japanese urban legends
Programmed all of the game’s core function except for ‘Part I’ of the game.
Link: Geisha - Roblox



Timezone, GMT -8


Prices are negotiable; payment only with either Paypal or funds.
A down payment of 50% will be preferred.


I’ll appreciate every opportunity I get to work with others in hopes of diversifying my portfolio.
That said, I am up for any challenges.

Drop me a direct message on this site.


My GitHub profile

37 Likes

Really like your work! However, I’m worried that you’re under-valuing yourself way too much! At the current DevEx rates, $1 is the equivalent to about 285 robux, and the average minimum wage in the United States is about $9/hr, so even if you wanted to provide work on the cheaper end, you’d have to charge around $7/hr, which equates to about 2,000 Robux/hr.

Either way, however you decide to value your work, amazing work! :slight_smile:

2 Likes

What’s a tempura? I’m just curious. :thinking:

2 Likes

Just made a small order with @FadedJayden_Dev and ended up getting my models fairly quickly for the amount of work that needed doing and got a lot for my money.

I would recommend hiring him for any of your needs, especially since he was flexible and went the extra mile by providing me with high and low poly versions of the meshes to choose between!

9 Likes

Vouch for this user. Very efficient in his work, open to feedback, and good at designing what he does in Blender. Would definitely recommend him to anyone who needs any models done and would definitely commission him again for any future projects. Best of luck on your future endeavors!

Regards, Freeze

2 Likes

Extremely professional, open to feedback, and willing to explore new ways of modelling individual! Will definitely be commissioning him again in the future, and can vouch for how good he is.

2 Likes

Chill dude, love that my brotha!

2 Likes

Great to work with! He is extremely fast, reliably and cheap! Highly recommend!

1 Like

Hello! I love your work! I am looking for a low poly modeler! Would you be interested? Hiring a low poly car modeler

hey bro i wan to work for you.

Want to elaborate on that?
Don’t quite get your meaning.

Hi, interested in working with you. Sent a friend request under andrew.#0001.

Amazing work, love the coke render so realistic! Would love to see you grow further in the modeling world.

1 Like

Much appreciated for the comment! :)))

Hi are your commissions currently open? I sent a friend request under GoldenLoDev#9189 and I pay in USD or group funds :slight_smile:

1 Like

Your work is really good! Add me at: SolarFlash12#6405. We’ll discuss the prices, what you’ll be modeling, and what you’ll be scripting on there! Hope to see you soon!

1 Like

Also, I changed my character to this as a meme, and now I can’t change it back

1 Like

I sent you a friend request on discord as I’d like to get you to model some things for me, my discord is Vinnyy#0001

Hi! I recently commissioned F_d3d, and was amazed by the work. Everything was exactly as wanted, and given in roughly 2 - 3 days. Very fast service & kind service as well.
I would recommend hiring F_d3d also for the low prices. Thank you again!

Sincerely,
Andrew

1 Like

@F_d3d When do you think you will open commissions back?

1 Like