কেস স্টাডি - HTML5 ক্যানভাসের সাথে জড়িত হওয়া

ভূমিকা

এই গত বসন্তে (2010) আমি HTML5 এবং সম্পর্কিত প্রযুক্তিগুলির জন্য দ্রুত বর্ধিত সমর্থনে আগ্রহ নিয়েছিলাম। সেই সময়ে, একজন বন্ধু এবং আমি আমাদের প্রোগ্রামিং এবং বিকাশের দক্ষতাগুলিকে আরও উন্নত করার জন্য এবং আমরা ক্রমাগত একে অপরের দিকে ছুঁড়ে ফেলা গেমের ধারণাগুলিকে জীবিত করতে দুই সপ্তাহের গেম ডেভেলপমেন্ট প্রতিযোগিতায় একে অপরকে চ্যালেঞ্জ করছিলাম। সুতরাং, আমি স্বাভাবিকভাবেই আমার প্রতিযোগিতার এন্ট্রিগুলিতে HTML5 উপাদানগুলিকে অন্তর্ভুক্ত করা শুরু করেছি যাতে তারা কীভাবে কাজ করে তার সম্পর্কে আরও ভাল বোঝার জন্য এবং আগের HTML স্পেসগুলি ব্যবহার করে যেগুলি প্রায় অসম্ভব ছিল তা করতে সক্ষম হতে পারি৷

HTML5-এর অনেক নতুন বৈশিষ্ট্যের মধ্যে, ক্যানভাস ট্যাগের জন্য ক্রমবর্ধমান সমর্থন আমাকে জাভাস্ক্রিপ্ট ব্যবহার করে ইন্টারেক্টিভ আর্ট বাস্তবায়নের একটি উত্তেজনাপূর্ণ সুযোগ দিয়েছে, যা আমাকে এখন এনট্যাঙ্গলমেন্ট নামে একটি ধাঁধা খেলা বাস্তবায়ন করার চেষ্টা করতে পরিচালিত করেছে। আমি ইতিমধ্যেই সেটলার অফ ক্যাটান টাইলসের পিছনের অংশ ব্যবহার করে একটি প্রোটোটাইপ তৈরি করেছি, তাই এটিকে একটি ব্লুপ্রিন্ট হিসাবে ব্যবহার করে, ওয়েব প্লের জন্য HTML5 ক্যানভাসে ষড়ভুজ টাইল তৈরি করার জন্য তিনটি অপরিহার্য অংশ রয়েছে: ষড়ভুজ আঁকা, পথ আঁকা, এবং টালি ঘোরানো. নিম্নলিখিত বিশদ রূপরেখার মধ্যে যায় কিভাবে আমি তাদের বর্তমান আকারে এই প্রতিটি সম্পন্ন করেছি।

ষড়ভুজ আঁকা

এনট্যাঙ্গলমেন্টের মূল সংস্করণে, আমি ষড়ভুজ আঁকার জন্য বেশ কয়েকটি ক্যানভাস ড্র পদ্ধতি ব্যবহার করেছি, কিন্তু গেমটির বর্তমান ফর্মটি একটি স্প্রাইট শীট থেকে ক্লিপ করা টেক্সচার আঁকার জন্য drawImage() ব্যবহার করে।

টাইলস স্প্রাইট শীট
টাইলস স্প্রাইট শীট

আমি ছবিগুলিকে একক ফাইলে একত্রিত করেছি তাই এই ক্ষেত্রে দশটির পরিবর্তে সার্ভারের কাছে শুধুমাত্র একটি অনুরোধ। ক্যানভাসে একটি নির্বাচিত ষড়ভুজ আঁকতে, আমাদের প্রথমে আমাদের সরঞ্জামগুলিকে একত্রিত করতে হবে: ক্যানভাস, প্রসঙ্গ এবং চিত্র।

একটি ক্যানভাস তৈরি করতে, আমাদের যা প্রয়োজন তা হল আমাদের এইচটিএমএল নথিতে ক্যানভাস ট্যাগ:

<canvas id="myCanvas"></canvas>

আমি এটিকে একটি আইডি দিই যাতে আমরা এটিকে আমাদের স্ক্রিপ্টে টানতে পারি:

var cvs = document.getElementById('myCanvas');

দ্বিতীয়ত, আমাদের ক্যানভাসের জন্য 2d প্রসঙ্গ ধরতে হবে যাতে আমরা অঙ্কন শুরু করতে পারি:

var ctx = cvs.getContext('2d');

সবশেষে, আমাদের ছবিটি দরকার। আমাদের ওয়েব পৃষ্ঠার মতো একই ফোল্ডারে এটির নাম "tiles.png" থাকলে, আমরা এটি এর মাধ্যমে পেতে পারি:

var img = new Image();
img.src = 'tiles.png';

এখন যেহেতু আমাদের কাছে তিনটি উপাদান রয়েছে, আমরা স্প্রাইট শীট থেকে ক্যানভাসে যে একক ষড়ভুজটি চাই তা আঁকতে আমরা ctx.drawImage() ব্যবহার করতে পারি:

ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
            destinationX, destinationY, destinationWidth, destinationHeight);

এই ক্ষেত্রে, আমরা উপরের সারিতে বাম থেকে চতুর্থ ষড়ভুজ ব্যবহার করছি। এছাড়াও, আমরা এটিকে উপরের বাম কোণে ক্যানভাসে আঁকব, এটি মূল আকারের মতোই রাখব। অনুমান করা হচ্ছে ষড়ভুজগুলি 400 পিক্সেল চওড়া এবং 346 পিক্সেল উচ্চ, সব মিলিয়ে এটি দেখতে এরকম কিছু হবে:

var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';
var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
            destinationX, destinationY, destinationWidth, destinationHeight);

আমরা এর ফলাফল হিসাবে সফলভাবে ছবির অংশ ক্যানভাসে কপি করেছি:

হেক্সাগোনাল টালি
হেক্সাগোনাল টালি

পথ অঙ্কন

এখন আমাদের ষড়ভুজটি ক্যানভাসে আঁকা হয়েছে, আমরা এটিতে কয়েকটি লাইন আঁকতে চাই। প্রথমে, আমরা ষড়ভুজ টাইল সম্পর্কিত কিছু জ্যামিতি দেখব। আমরা প্রতি পাশে দুটি লাইনের প্রান্ত চাই প্রতিটি প্রান্ত বরাবর প্রান্ত থেকে 1/4 এবং প্রান্তের 1/2টি একে অপরের থেকে আলাদা, যেমন:

ষড়ভুজ টাইলের রেখার শেষ বিন্দু
ষড়ভুজ টাইলের রেখার শেষ বিন্দু

আমরা একটি সুন্দর বক্ররেখাও চাই, তাই, একটু ট্রায়াল এবং ত্রুটি ব্যবহার করে, আমি খুঁজে পেয়েছি যে, যদি আমি প্রতিটি প্রান্তের প্রান্ত থেকে একটি লম্ব রেখা তৈরি করি, তাহলে ষড়ভুজের একটি প্রদত্ত কোণের চারপাশে প্রতিটি জোড়া প্রান্তবিন্দু থেকে ছেদটি একটি সুন্দর করে তোলে। প্রদত্ত এন্ডপয়েন্টের জন্য বেজিয়ার কন্ট্রোল পয়েন্ট:

ষড়ভুজ টাইলের উপর নিয়ন্ত্রণ পয়েন্ট
ষড়ভুজ টাইলের উপর নিয়ন্ত্রণ পয়েন্ট

এখন, আমরা আমাদের ক্যানভাস চিত্রের সাথে সঙ্গতিপূর্ণ একটি কার্টেসিয়ান সমতলে শেষ বিন্দু এবং নিয়ন্ত্রণ পয়েন্ট উভয়ই ম্যাপ করি এবং আমরা কোডে ফিরে যেতে প্রস্তুত। এটি সহজ রাখতে, আমরা একটি লাইন দিয়ে শুরু করব। আমরা উপরের বাম প্রান্ত থেকে নীচের ডান প্রান্ত বিন্দুতে একটি পথ অঙ্কন করে শুরু করব। আমাদের পূর্বের ষড়ভুজ চিত্রটি 400x346 হওয়ায়, এটি আমাদের শীর্ষ প্রান্তবিন্দুকে 150 পিক্সেল জুড়ে এবং 0 পিক্সেল নিচে, শর্টহ্যান্ড (150, 0) করবে। এর কন্ট্রোল পয়েন্ট হবে (150, 86)। নীচের প্রান্তের প্রান্তটি হল (250, 346) একটি নিয়ন্ত্রণ বিন্দু সহ (250, 260):

প্রথম বেজিয়ার বক্ররেখার জন্য স্থানাঙ্ক
প্রথম বেজিয়ার বক্ররেখার জন্য স্থানাঙ্ক

আমাদের স্থানাঙ্ক হাতে রেখে, আমরা এখন অঙ্কন শুরু করার জন্য প্রস্তুত। আমরা ctx.beginPath() দিয়ে নতুন করে শুরু করব এবং তারপর ব্যবহার করে প্রথম এন্ডপয়েন্টে চলে যাব:

ctx.moveTo(pointX1,pointY1);

তারপরে আমরা ctx.bezierCurveTo() ব্যবহার করে লাইনটি নিজেই আঁকতে পারি:

ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);

যেহেতু আমরা লাইনের একটি সুন্দর সীমানা চাই, তাই আমরা প্রতিবার ভিন্ন প্রস্থ এবং রঙ ব্যবহার করে এই পথটিকে দুবার স্ট্রোক করব। ctx.strokeStyle প্রপার্টি ব্যবহার করে রঙ সেট করা হবে এবং ctx.lineWidth ব্যবহার করে প্রস্থ সেট করা হবে। সর্বোপরি, প্রথম লাইন অঙ্কন এই মত হবে:

var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.beginPath();
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

আমাদের কাছে এখন একটি ষড়ভুজ টাইল রয়েছে যার প্রথম লাইনটি জুড়ে রয়েছে:

ষড়ভুজ টাইলের নির্জন রেখা
ষড়ভুজ টাইলের নির্জন রেখা

অন্যান্য 10টি শেষ পয়েন্টের পাশাপাশি সংশ্লিষ্ট বেজিয়ার কার্ভ কন্ট্রোল পয়েন্টগুলির জন্য স্থানাঙ্ক প্রবেশ করানো, আমরা উপরের পদক্ষেপগুলি পুনরাবৃত্তি করতে পারি এবং এর মতো একটি টাইল তৈরি করতে পারি:

সম্পূর্ণ ষড়ভুজাকার টালি।
সম্পূর্ণ ষড়ভুজাকার টালি

ক্যানভাস ঘোরানো

একবার আমাদের টাইল হয়ে গেলে, আমরা এটিকে ঘুরিয়ে দিতে সক্ষম হতে চাই যাতে গেমটিতে বিভিন্ন পথ নেওয়া যায়। ক্যানভাস ব্যবহার করে এটি সম্পন্ন করতে, আমরা ctx.translate() এবং ctx.rotate() ব্যবহার করি। আমরা চাই টাইলটি তার কেন্দ্রের চারপাশে ঘোরুক, তাই আমাদের প্রথম ধাপ হল ক্যানভাস রেফারেন্স পয়েন্টটিকে হেক্সাগোনাল টাইলের কেন্দ্রে নিয়ে যাওয়া। এটি করার জন্য আমরা ব্যবহার করি:

ctx.translate(originX, originY);

যেখানে originX হবে হেক্সাগোনাল টাইলের প্রস্থের অর্ধেক এবং OriginY হবে উচ্চতার অর্ধেক, আমাদের দেয়:

var originX = 200;
var originY = 173;
ctx.translate(originX, originY);

এখন আমরা আমাদের নতুন কেন্দ্র বিন্দু দিয়ে টাইল ঘোরাতে সক্ষম। যেহেতু একটি ষড়ভুজ ছয়টি বাহু আছে, তাই আমরা এটিকে গণিতের কিছু মাল্টিপল দিয়ে ঘোরাতে চাই। PI 3 দ্বারা বিভক্ত। আমরা এটিকে সরল রাখব এবং ঘড়ির কাঁটার দিকে একটি একক বাঁক নিয়ে যাবো:

ctx.rotate(Math.PI / 3);

যাইহোক, যেহেতু আমাদের ষড়ভুজ এবং লাইনগুলি পুরানো (0,0) স্থানাঙ্কগুলিকে উত্স হিসাবে ব্যবহার করছে, একবার আমরা ঘোরানো শেষ হয়ে গেলে, আমরা অঙ্কন করার আগে আবার অনুবাদ করতে চাই। সুতরাং, সামগ্রিকভাবে আমাদের এখন আছে:

var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);

আমাদের রেন্ডারিং কোডের আগে উপরের অনুবাদ এবং ঘূর্ণনটি রাখলে এটি এখন ঘোরানো টাইল রেন্ডার করে:

ঘোরানো ষড়ভুজাকার টালি
ঘোরানো ষড়ভুজাকার টালি

সারসংক্ষেপ

উপরে আমি কিছু ক্ষমতা হাইলাইট করেছি যা HTML5 ক্যানভাস ট্যাগ ব্যবহার করে অফার করে, যার মধ্যে ছবি রেন্ডার করা, বেজিয়ার কার্ভ আঁকা এবং ক্যানভাস ঘোরানো। এইচটিএমএল 5 ক্যানভাস ট্যাগ এবং এর জাভাস্ক্রিপ্ট অঙ্কন সরঞ্জামগুলি এনট্যাঙ্গলমেন্টের জন্য ব্যবহার করা একটি উপভোগ্য অভিজ্ঞতা হিসাবে প্রমাণিত হয়েছে, এবং আমি এই উন্মুক্ত এবং উদীয়মান প্রযুক্তির সাথে অন্যরা তৈরি করা অনেকগুলি নতুন অ্যাপ্লিকেশন এবং গেমের অপেক্ষায় রয়েছি।

কোড রেফারেন্স

উপরে প্রদত্ত সমস্ত কোড উদাহরণ একটি রেফারেন্স হিসাবে নীচে একত্রিত করা হয়েছে:

var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';

var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);

var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
            destinationX, destinationY, destinationWidth, destinationHeight);

ctx.beginPath();
var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

ctx.beginPath();
pointX1 = 250;
pointY1 = 0;
controlX1 = 250;
controlY1 = 86;
controlX2 = 150;
controlY2 = 86;
pointX2 = 75;
pointY2 = 43;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

ctx.beginPath();
pointX1 = 150;
pointY1 = 346;
controlX1 = 150;
controlY1 = 260;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

ctx.beginPath();
pointX1 = 325;
pointY1 = 43;
controlX1 = 250;
controlY1 = 86;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 130;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

ctx.beginPath();
pointX1 = 25;
pointY1 = 130;
controlX1 = 100;
controlY1 = 173;
controlX2 = 100;
controlY2 = 173;
pointX2 = 25;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();

ctx.beginPath();
pointX1 = 325;
pointY1 = 303;
controlX1 = 250;
controlY1 = 260;
controlX2 = 150;
controlY2 = 260;
pointX2 = 75;
pointY2 = 303;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();