2025 এর জন্য আমার লক্ষ্যগুলির মধ্যে একটি হল একটি সম্পূর্ণ গেম তৈরি করা। সম্পূর্ণ যেমন আছে, আপনি এটি স্টিম বা অ্যাপ স্টোরে $2.99 বা তার বেশি দামে কিনতে পারেন। আমি আগে ছোট গেম তৈরি করেছি কিন্তু একটি গেম সম্পূর্ণ করা এবং শিপিং করা সম্ভবত আমার সবচেয়ে বড় সাইড প্রজেক্ট হতে পারে (এই ব্লগটি বাদে)।
শীতের বিরতিতে, আমি গেমের প্রোটোটাইপ তৈরিতে কিছু সময় কাটিয়েছি সিংহ — লুয়াতে 2D গেম তৈরির জন্য একটি কাঠামো। আমার লক্ষ্য ছিল কোন গেম তৈরির সরঞ্জামগুলি আমার দক্ষতার সাথে মানানসই হয় তা নিয়ে গবেষণা করা এবং আমার শক্তিগুলি কোথায় রয়েছে তা খুঁজে বের করা যাতে আমি 2025 সালে আমার সময় দিয়ে দক্ষ হতে পারি।
এই প্রোটোটাইপগুলিতে কাজ করার আগে আমি লুয়ার প্রায় 200LOC লিখেছিলাম কিন্তু আমার প্রয়োজনীয় সিনট্যাক্স বাছাই করতে আমার কোন সমস্যা ছিল না।
আমি LÖVE এর API সহজ এবং শক্তিশালী বলে মনে করেছি। একটি ব্যবহার করার সুবিধাগুলির মধ্যে একটি কাঠামো একটি গেম ইঞ্জিনের উপরে হল যে আমি আপনাকে 10LOC সহ একটি সম্পূর্ণ উদাহরণ দেখাতে পারি (একটি গেম ইঞ্জিনের বিপরীতে, যেখানে আমাকে দৃশ্য অবজেক্টগুলি সংজ্ঞায়িত করতে হবে, স্ক্রিপ্ট সংযুক্ত করতে হবে এবং আরও অনেক কিছু)।
এই স্নিপেটটি একজন খেলোয়াড়কে স্ক্রীন জুড়ে একটি বর্গক্ষেত্র সরানোর অনুমতি দেয়।
x = 100
-- update the state of the game every frame
---@param dt number time since the last update in seconds
function love.update(dt)
if love.keyboard.isDown('space') then
x = x + 200 * dt
end
end
-- draw on the screen every frame
function love.draw()
love.graphics.setColor(1, 1, 1)
love.graphics.rectangle('fill', x, 100, 50, 50)
end
যদিও আমার প্রোটোটাইপগুলি এর চেয়ে বেশি পরিপূর্ণ ছিল, এই স্নিপেটটি LÖVE এর সারমর্মকে ক্যাপচার করে।
দাবা UI
আমি প্রতি শীতে দাবাতে ফিরে আসি। খেলা, উন্নতি করার চেষ্টা করা, এবং দাবা-সম্পর্কিত প্রকল্প গ্রহণ করা (এই সময়ে প্রায় চার বছর আগে, আমি একটি দাবা ইঞ্জিন তৈরি করেছি)।
প্রধান দাবা খেলোয়াড়দের UIs (chess.com, lichess.org) অবিশ্বাস্যভাবে ভাল চিন্তা করা হয়. একটি দাবা UI একটি সাধারণ সমস্যা বলে মনে হতে পারে কিন্তু যখন আমি রাষ্ট্রীয় পরিবর্তনের মধ্য দিয়ে যেতে শুরু করি, তখন আমি বুঝতে পেরেছিলাম যে এটি একসাথে কতটা সুন্দরভাবে ফিট করে। lichess.org-এ পোস্ট-গেম বিশ্লেষণ UI বিশেষভাবে ভালো।
আমি দাবা ধাঁধার উপর একটি রিফ তৈরি করতে চেয়েছিলাম কিন্তু প্রথমে আমাকে একটি বেসলাইন দাবা UI কাজ করতে হবে। এটি ছিল আমার প্রথম প্রেমের প্রোগ্রাম, এবং এতে আমার প্রায় দুই ঘণ্টা সময় লেগেছে।
মাউস ইনপুট ক্যাপচার করতে, আমি LÖVE এর কলব্যাক ফাংশনগুলির একটি মিশ্রণ ব্যবহার করেছি (love.mousereleased
একটি টানা শেষের জন্য, love.mousepressed
দুই ক্লিকে একটি টুকরা সরাতে)।
আমি ব্যবহার করেছি love.mouse.getPosition()
টুকরা রেন্ডার করার জন্য যখন তারা টেনে নিয়ে যাওয়া হচ্ছিল।
local pieceImage = love.graphics.newImage("assets/chess_" .. piece.name .. ".png")
-- ..
-- draw dragged piece at cursor position
if piece.dragging then
local mouseX, mouseY = love.mouse.getPosition()
-- center the piece on cursor
local floatingX = mouseX - (pieceImage:getWidth() * scale) / 2
local floatingY = mouseY - (pieceImage:getHeight() * scale) / 2
-- draw the floating piece with correct color
if piece.color == "white" then
love.graphics.setColor(1, 1, 1)
else
love.graphics.setColor(0.2, 0.2, 0.2)
end
love.graphics.draw(pieceImage, floatingX, floatingY, 0, scale, scale)
end
আমি বছরের পর বছর ধরে অনেক লাইব্রেরির সাথে UI তৈরি করেছি। LÖVE ব্যবহার করার সবচেয়ে তুলনামূলক অভিজ্ঞতা সম্ভবত ব্রাউজারের ক্যানভাস API. কোড সহ ফ্রি-ফর্ম UI প্রোটোটাইপ করার জন্য আমি LÖVE কে সেরা সমাধান বলে মনে করি। আমি বলি free-form কারণ যদি আমার ইনপুট এবং বোতাম সহ কিছু প্রয়োজন হয় তবে আমি মনে করি না LÖVE একটি ভাল পছন্দ হবে।
LÖVE কে এমন একটি শক্তিশালী সমাধান করার একটি কারণ হল যে LÖVE-এর সাথে প্রোটোটাইপ তৈরি করার জন্য প্রয়োজনীয় কোড তৈরি এবং বিশ্লেষণ করার জন্য LLM-এর একটি সহজ সময় থাকে। APIটি সুপরিচিত (অথবা খুব সংক্ষিপ্ত ডকস্ট্রিংগুলির সাথে যোগাযোগ করা যেতে পারে) এবং বাকি কোডটি জেনেরিক UI গণিত।
এটি Godot Engine-এর GDScript-এর বিরোধিতা করে যা LLM-গুলিকে বাইরের-অফ-দ্য-বক্সের সাথে লড়াই করে বলে মনে হয়। আমি কল্পনা করি যে এটি যেমন জিনিসগুলির সাথে উন্নত করা যেতে পারে: ফাইন-টিউনিং, আরএজি (পুনরুদ্ধার-অগমেন্টেড জেনারেশন), বা কয়েকটি-শট প্রম্পটিং — তবে আমি এটি আরও অন্বেষণ করিনি।
আমি আগে ভিজ্যুয়াল প্রজেক্টে এলএলএম ব্যবহার করিনি এবং আমি কতটা ঘনিষ্ঠভাবে অবাক হয়েছিলামclaude-3.5-sonnet
এবং gpt-4o
আমার প্রম্পট পেতে সক্ষম হয়েছিল (এর মাধ্যমে কার্সার)
যদিও LÖVE প্রোগ্রামগুলি খুব দ্রুত খোলে, আমি এখনও ব্রাউজার UI-তে কাজ করার সময় আপনি যে হট রিলোডিং পান তা মিস করি। একটি বড় প্রকল্পে, আমি সম্ভবত একটি ডিবাগ ভিউ এবং/অথবা UI কনফিগারেশনের হট রিলোডিং তৈরিতে কিছু সময় বিনিয়োগ করব।
আমি আমার UI লজিক বনাম অ্যাপ্লিকেশন লজিকের বিচ্ছেদ নিয়ে কিছুটা সংগ্রাম করেছি। আমি অনুভব করিনি যে আমি একটি বিশেষভাবে পরিষ্কার বিচ্ছেদ নিয়ে শেষ করেছি তবে এটির সাথে কাজ করা উত্পাদনশীল ছিল। আপনি নীচের উদ্ধৃতাংশে আমি কিভাবে আমার “টুকরা API” গ্রাস করতে পারেন দেখতে পারেন.
-- called when a mouse button is pressed
---@param x number x coordinate of the mouse
---@param y number y coordinate of the mouse
function love.mousepressed(x, y, button)
local result = xyToGame(x, y)
-- check if we've clicked on a valid square
if result.square then
for _, piece in ipairs(pieces) do
-- if we have a piece clicked and it's a valid square, move it
if piece.clicked and piece:validSquare(result.square) then
piece:move(result.square)
return
end
end
end
-- check if we've clicked on a piece
if result.piece then
result.piece:click(x, y)
result.piece:drag()
return
end
-- otherwise, unclick all pieces
for _, piece in ipairs(pieces) do
piece:unclick()
end
end
কার্ড গেম UI
আরেকটি UI যা আমি সম্প্রতি সম্পর্কে চিন্তা করছি চুলা পাথর যেটা আমি মুক্তির পর প্রায় এক বছর খেলেছিলাম। এটি একটি প্রতিযোগিতামূলক কার্ড গেমের সাথে আমার প্রথম অভিজ্ঞতা ছিল এবং আমি এটির সাথে অনেক মজা করেছি।
বাস্তবায়ন জটিলতার ক্ষেত্রে কার্ড গেমগুলি একটি মিষ্টি জায়গায় বিদ্যমান বলে মনে হচ্ছে। কাজের বড় অংশ পরিকল্পনা এবং গেম ডিজাইন বলে মনে হচ্ছে। 3D গেমের বিপরীতে, যেখানে শিল্প এবং গেমের জগত তৈরি করতে একটি উল্লেখযোগ্য পরিমাণ সময় প্রয়োজন। আমার ব্যক্তিগত অনুভূতি হল যে আমি প্রায় এক মাসের মধ্যে একটি ইতিমধ্যে-পরিকল্পিত কার্ড গেম এমভিপি তৈরি করতে পারি।
এই প্রোটোটাইপটি আমার তিন ঘন্টা সময় নিয়েছে।
দাবা UI-এর তুলনায়, এই কার্ড গেমের প্রোটোটাইপের জন্য LOC-এর দ্বিগুণের একটু বেশি প্রয়োজন। মসৃণ কার্ড ইন্টারঅ্যাকশন অ্যানিমেশন রেন্ডার করার সময় আমি আমার প্রথম কিছু চ্যালেঞ্জের মুখোমুখি হয়েছিলাম।
আমি সাধারণত একটি প্রোটোটাইপে অ্যানিমেশন যোগ করা এড়াতে পারি তবে সেগুলি একটি ভাল অনুভূতির কার্ড গেমের মূল তাই আমি সেগুলিকে প্রোটোটাইপ পর্যায়ে এগিয়ে নিয়ে এসেছি।
দাবা ইউআই-এর মতোই, এলএলএমগুলি কিছু সহজ ভারার কাজে সাহায্য করতে সক্ষম হয়েছিল যেমন বাক্স এবং পাঠ্য সঠিক জায়গায় আঁকা, এবং কিছু বিক্ষিপ্ত অবস্থাকে কনফিগারেশনের দুটি গ্রুপে (গেম কনফিগারেশন এবং গেম স্টেট) সংগ্রহ করা।
স্বাস্থ্য এবং মানা বারগুলির মতো সাধারণ জিনিসগুলির ক্ষেত্রে, LÖVE সত্যিই উজ্জ্বল হয়৷
local function drawResourceBar(x, y, currentValue, maxValue, color)
-- background
love.graphics.setColor(0.2, 0.2, 0.2, 0.8)
love.graphics.rectangle("fill", x, y, Config.resources.barWidth, Config.resources.barHeight)
-- fill
local fillWidth = (currentValue / maxValue) * Config.resources.barWidth
love.graphics.setColor(color(1), color(2), color(3), 0.8)
love.graphics.rectangle("fill", x, y, fillWidth, Config.resources.barHeight)
-- border
love.graphics.setColor(0.3, 0.3, 0.3, 1)
love.graphics.setLineWidth(Config.resources.border)
love.graphics.rectangle("line", x, y, Config.resources.barWidth, Config.resources.barHeight)
-- value text
love.graphics.setColor(1, 1, 1)
local font = love.graphics.newFont(12)
love.graphics.setFont(font)
local text = string.format("%d/%d", currentValue, maxValue)
local textWidth = font:getWidth(text)
local textHeight = font:getHeight()
love.graphics.print(text,
x + Config.resources.barWidth/2 - textWidth/2,
y + Config.resources.barHeight/2 - textHeight/2
)
end
local function drawResourceBars(resources, isOpponent)
local margin = 20
local y = isOpponent and margin or
love.graphics.getHeight() - margin - Config.resources.barHeight * 2 - Config.resources.spacing
drawResourceBar(margin, y, resources.health, Config.resources.maxHealth, {0.8, 0.2, 0.2})
drawResourceBar(margin, y + Config.resources.barHeight + Config.resources.spacing,
resources.mana, resources.maxMana, {0.2, 0.2, 0.8})
end
কার্ডগুলির অ্যানিমেশনগুলি (হোভারের সময় বাড়তে বা বাড়তে, নেমে গেলে হাতের কাছে পড়ে) আমার প্রয়োজনীয়তাগুলি সংজ্ঞায়িত করার পরে তৈরি করা খুব কঠিন ছিল না।
-- update the state of the game every frame
---@param dt number time since the last update in seconds
function love.update(dt)
-- ..
-- update card animations
for i = 1, #State.cards do
local card = State.cards(i)
if i == State.hoveredCard and not State.draggedCard then
updateCardAnimation(card, Config.cards.hoverRise, Config.cards.hoverScale, dt)
else
updateCardAnimation(card, 0, 1, dt)
end
updateCardDrag(card, dt)
end
end
-- lerp card towards a target rise and target scale
local function updateCardAnimation(card, targetRise, targetScale, dt)
local speed = 10
card.currentRise = card.currentRise + (targetRise - card.currentRise) * dt * speed
card.currentScale = card.currentScale + (targetScale - card.currentScale) * dt * speed
end
-- lerp dragged cards
local function updateCardDrag(card, dt)
if not State.draggedCard then
local speed = 10
card.dragOffset.x = card.dragOffset.x + (0 - card.dragOffset.x) * dt * speed
card.dragOffset.y = card.dragOffset.y + (0 - card.dragOffset.y) * dt * speed
end
end
উপরের কোডটি লক্ষ্য মানের মধ্যে তাদের বৃদ্ধি/স্কেল বৈশিষ্ট্যগুলিকে মসৃণভাবে রূপান্তর করে আমার কার্ডগুলিকে অ্যানিমেট করে। রৈখিক ইন্টারপোলেশন (লারপিং) এর একটি সর্বোত্তম উদাহরণ যেখানে বর্তমান মানগুলি ধীরে ধীরে অতিবাহিত সময় এবং একটি গতি গুণকের উপর ভিত্তি করে লক্ষ্য মানের দিকে সরানো হয়।
যেখানে আমি এখান থেকে যাই
এই প্রোটোটাইপগুলি তৈরি করার পরে (পাশাপাশি কিছু অন্যান্য ছোটগুলি এখানে কভার করা হয়নি), আমার কাছে LÖVE-এর সাথে তৈরি করা আমার জন্য ফলদায়ক হবে এমন প্রকল্পগুলির উপর আমার বেশ ভাল ধারণা রয়েছে।
আমি Godot ইঞ্জিনের সাথে খেলার জন্য কিছু সময় কাটিয়েছি কিন্তু এখনও আমার নোট লিখিনি। টিএল;ডিআর এমন কিছু: যদি আমার গেম ইঞ্জিন বৈশিষ্ট্যগুলির প্রয়োজন হয় (খুব ব্যস্ত বিশ্ব, জটিল সত্তা মিথস্ক্রিয়া, মৌলিক বিষয়ের বাইরে পদার্থবিদ্যা) আমি গডটের কাছে পৌঁছাব।
2025 এর জন্য আমার আলগা প্রকল্প পরিকল্পনা এইরকম কিছু দেখায়:
- নোটবুক/পেন দিয়ে একটি গেম ডিজাইন করুন
- কাগজের বাইরে গেমটি তৈরি করুন এবং আমার স্ত্রীর সাথে প্রোটোটাইপটি খেলুন
- একটি মৌলিক MVP তৈরি করুন (কোনও শিল্প ছাড়াই)
- বন্ধুদের সাথে খেলার পরীক্ষা
- পুনরাবৃত্তি/আরো প্লেটেস্টিং
- শিল্প তৈরি করুন
- ???
- জাহাজ
আমি আমার প্রোটোটাইপ কোড অত্যধিক দরকারী হতে আশা করি না কিন্তু এটা ওপেন সোর্স তবুও!