A cheap way to hit test unregular area
2013-10-21 22:26
253 查看
Hit-testing an unregular area is important when you're creating some game UI. Here is a cheap way. When I say cheap, it's extremely fast, and has very tiny memory foot print.
Here is the idea:
1. Write a program to process the image which represents the unregular area, and produce some binary data for easy hit-testing.
2. In the app, when hit-testing is required, simply check the corresponding bit.
Preparing the data is simple. I used C# to complete this task.
This little program reads the picture file, check every pixel's transparency, and set visible enough pixel's bit to 1.
Now in the game app, we can check it very easily. Here is the HitArea class:
Very easy and extremely fast. Isn't it?
Here is the idea:
1. Write a program to process the image which represents the unregular area, and produce some binary data for easy hit-testing.
2. In the app, when hit-testing is required, simply check the corresponding bit.
Preparing the data is simple. I used C# to complete this task.
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.IO; namespace MakeHitArea { class Program { static void Main(string[] args) { foreach (string s in args) { using (HitAreaBuilder builder = new HitAreaBuilder(s)) { builder.Build(); } } } } public class HitAreaBuilder : IDisposable { Bitmap thePicture = null; string theOutName = string.Empty; int theWidth = 0; int theHeight = 0; byte[] theBaked = null; public HitAreaBuilder(string file) { try { using (Image image = Image.FromFile(file)) { thePicture = new Bitmap(image); } string path = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(path)) { path = Environment.CurrentDirectory; } string filename = Path.GetFileNameWithoutExtension(file); theOutName = string.Format("{0}\\{1}.hit", path, filename); } catch { Console.Error.WriteLine("Can't load image {0}", file); } } public void Dispose() { if (thePicture != null) { thePicture.Dispose(); thePicture = null; } } public void Build() { if (PrepareData()) { SaveData(); } } bool PrepareData() { bool succ = false; if (thePicture != null) { theWidth = thePicture.Width; theHeight = thePicture.Height; // Align the width for easier processing int linebytes = (theWidth + 7) / 8; theWidth = linebytes * 8; theBaked = new byte[linebytes * theHeight]; for (int y = 0; y < theHeight; y++) { for (int x = 0; x < linebytes; x++) { byte dat = 0; for (int bit = 0; bit < 8; bit++) { if (x * 8 + bit < thePicture.Width) { // If the pixel is visible enough, we set the bit to hit-able. Color c = thePicture.GetPixel(x * 8 + bit, y); if (c.A >= 200) { dat = (byte)(dat | (1 << bit)); } } } theBaked[y * linebytes + x] = dat; } } succ = true; } return succ; } void SaveData() { try { using (FileStream fs = new FileStream(theOutName, FileMode.Create, FileAccess.Write)) { using (BinaryWriter writer = new BinaryWriter(fs)) { writer.Write((short)theWidth); writer.Write((short)theHeight); writer.Write(theBaked); } } } catch { Console.Error.WriteLine("Write to file failed."); } } } }
This little program reads the picture file, check every pixel's transparency, and set visible enough pixel's bit to 1.
Now in the game app, we can check it very easily. Here is the HitArea class:
class HitArea { public: HitArea(const std::string& file); ~HitArea(); bool test(int x, int y); private: HitArea(); HitArea(const HitArea&); HitArea& operator= (const HitArea&); protected: short _width; short _height; unsigned char* _area; }; HitArea::HitArea(const std::string& file) : _width(0) , _height(0) , _area(NULL) { std::ifstream fs(file.c_str(), std::ios::in | std::ios::binary); if (fs) { fs.read((char*)&_width, sizeof(_width)); fs.read((char*)&_height, sizeof(_height)); int size = _width / 8 * _height; _area = new unsigned char[size]; fs.read((char*)_area, size); } } HitArea::~HitArea() { if (_area) delete[] _area; } bool HitArea::test(int x, int y) { if (x < 0 || x >= _width || y < 0 || y > _height) return false; int bytesPerLine = _width / 8; int hitByte = bytesPerLine * y + x / 8; int hitBit = x % 8; return !!((_area[hitByte] >> hitBit) & 1); }
Very easy and extremely fast. Isn't it?
相关文章推荐
- The Best Way to Unit Test in Android
- A simple way to roll back DB pollution in Test
- Way to Team Leader
- Go语言学习之运算符(The way to go)
- How To Troubleshoot Oracle Redo Log Reading Extract Slow Performance Issue using TESTMAPPINGSPEED (文
- best way to get something done at work
- Go语言学习之函数(The way to go)
- 《The way to go》中文版
- 吴恩达when to change dev/test sets and metrics
- POJ 2891-Strange Way to Express Integers(扩展欧几里德)
- Failed to resolve: com.android.support.test:runner:25.0.1处理办法
- What is the best way to port from Objective-C to C++?
- poj 2891 Strange Way to Express Integers
- How to Simulate Different Network Speeds in Your JMeter Load Test
- enough is enough - how to make a dmg the way you expect it
- Algorithm to find the area of a polygon
- android单元测试Test run failed: Unable to find instrumentation target package: com.abc.wallpaper.test
- A quick guide to VoIP on-the-cheap with Asterisk
- canvas save() restoreToCount() Test
- Vagrant is attempting to interface with the UI in a way that requires a TTY