Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411482 Posts in 69370 Topics- by 58426 Members - Latest Member: shelton786

April 23, 2024, 11:57:36 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsLxEdit
Pages: [1]
Print
Author Topic: LxEdit  (Read 3473 times)
Triplefox
Level 9
****



View Profile WWW
« on: July 17, 2009, 06:44:29 PM »

Yesterday I started writing a tool that integrates a sprite editor(sprite metadata, not painting) and a map editor(tiles, stamps, entities). It is my first PyQt4 app. I had an earlier incomplete version written within Flash but it was becoming obviously painful to use, so I bit the bullet and started anew with a environment that has real libraries and actual file support beyond prompting the user to load things one file at a time.


Above you can see my first progress: Some controls that do not work. You can open and create "projects" and it'll read the directory structure, though.

I will be using this for my own projects, so I have very specific functionality in mind:
  • Setting hotpoints and "pivot" (graphical center-of-mass) on sprites.
  • Stamping images on maps at arbitrary positions, scales, and rotations.
  • Tiles for collision/sector data on maps.
  • Entity prototypes - basically, property lists for each entity. I am planning to include one level of inheritance in this - every entity starts from a template and then properties may be modified per-instance to set position, scripts, et cetera.
  • Everything in JSON format with a naive, easy-to-parse, human-readable structure(Presumably, it can be compiled down for size/speed by the game's pipeline.)
  • Animation definition and previews(low priority feature, the game I'm doing has little animation).

At some point, probably after my basic functionality has settled down, I will open source it. I expect that to happen around the end of the month, if everything goes well. There are a lot of unknowns for me in that it's my first time with Qt, though.
Logged

george
Level 7
**



View Profile
« Reply #1 on: July 17, 2009, 06:58:30 PM »

  • Everything in JSON format with a naive, easy-to-parse, human-readable structure(Presumably, it can be compiled down for size/speed by the game's pipeline.)

I can't describe how happy I was to read JSON and not XML. this looks really promising.  Smiley
Logged
Triplefox
Level 9
****



View Profile WWW
« Reply #2 on: July 23, 2009, 03:04:12 PM »



The usage shown is kind of a lie, really. If you were making a fighting game with the tool as it is you'd get frustrated really quickly because there's no support for anims. But it should be good enough for the vehicle-centric game I'm making! Smiley

Here's how that sprite looks when saved:

Code:
{"pivot": [-8, -6], "image": "TerryPunch.PNG", "hotpoints": {"particle_spawn": [-55, 34], "body_br": [9, 30], "attack_tl": [41, -31], "body_tl": [-25, -23], "attack_br": [59, -10]}}

Next up is the map editor. I'm less of a noob with Qt than when I started, so hopefully that portion will go a little faster and have cleaner code than this. Stay tuned...
Logged

george
Level 7
**



View Profile
« Reply #3 on: July 23, 2009, 04:55:19 PM »

the interface is looking really clean, nice job.
Logged
Triplefox
Level 9
****



View Profile WWW
« Reply #4 on: July 28, 2009, 02:40:42 PM »

Progress report, nothing too much to show picture-wise:
  • Half-complete tile picker. It needs to allow tile renaming and color picking and then I'll call it "good enough."
  • Stamp picking. I settled on a system where you have a palette of three images available at all times, and you double-click to open a new dialog and change them. I could up this number later, three is my guess at the "minimum" that would be satisfying to use.
  • Some internal code refactoring. I still think it looks ugly, but oh well.
  • Started on the data model for stamps placed on the map and stamp layers. Doing this has made me realize that the map editor is probably 4x as complicated as the stamp editor, so it's unlikely that I will have it done at the end of the month.

And what I still have to implement, from my internal notes:
Code:
# Create and draw stamps on the map.
# Add a full set of controls for the stamp sidebar.
# Select and delete stamps.
# Move and rotate stamps.
# Create and draw tiles on the map.
# Add placeholder entity placement on the map.
# Add a full set of controls for the entity sidebar.
# Iterate over the entity placement.
# Add a full set of controls for the tile sidebar.
# Add multi-layer editing for each mode.
# Map save and load.

I'm going to target "sometime next week" for something release-ready. Coding this up is simultaneously boring and gratifying(few interesting problems, but lots of results).
Logged

skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #5 on: July 28, 2009, 11:49:05 PM »

Hats of to tool programmers who are generally very under appreciated, I salute you.  Gentleman

Keep up the interesting work. Hopefully you can maintain a high usability, meaning the learning curve is SMALL and it's easy and quick to use so that it actually is of any use Smiley So far looking good.
Logged

george
Level 7
**



View Profile
« Reply #6 on: July 29, 2009, 12:27:25 AM »

I'd like to hear your thoughts on Qt at some point too Triplefox if you're up for it.
Logged
Triplefox
Level 9
****



View Profile WWW
« Reply #7 on: July 29, 2009, 01:37:33 AM »

I'd like to hear your thoughts on Qt at some point too Triplefox if you're up for it.

I am always up for writing walls of text Big Laff

Qt is a strong library overall. Pretty much every widget you can think of is built in; the only customized drawing/painting I've done for this project is based on QGraphicsScene/QGraphicsView, which provide an abstract scenegraph. Wiring up events has been very little trouble with the "slots and signals" event model, I just had a small learning curve to figure out the conventions and how they translated to PyQt vs. C++ Qt. Layout is easy to code up using the box or grid models. I tried using the designer tool and failed utterly which was kind of expected. It seems like all the GUI designer tools I've used are useful only if you're making forms of fixed size and static layout.

The place where it's been ugliest is in matching the data model with the views, because Qt has its own data structures, and then Python has different data structures, and since the widgets expect the Qt model I tend to end up making "blessed" ways of changing any given data structure that synchronize all representations. And I don't think that problem could really be "fixed," either - figuring out the data model seems to be most of making a GUI app.

Here is an example of the kind of thinking I went through yesterday and today to figure out stamp placement:

To display all the stamps on the map I have to add their QGraphicsPixmapItem to a QGraphicsScene. Mostly straightforward: I load a file into a Pixmap and instance the Item with it, position it, rotate it, et cetera. The problem is that each Item has a zValue parameter that controls depth; the QGraphicsScene is unordered. But what I really want to represent the stamps with is an ordered list, as that improves consistency between the tool and the game: if everything has the same zValue, the view will display things in an undefined order, which is no good for background art. Maintaining a list avoids this problem.

So I have two models now, the QGraphicsScene internal model and the list, and I have to figure out the best means of synchronizing both. The method I'm settling on is to instance a Stamp class that contains the QGraphicsPixmapItem; the Item contains a backreference to the Stamp. The Stamp doesn't know what it belongs to, but it doesn't need to - the Item does, because the Item is what will be returned from mouse methods that look at the Scene. I also set a QGraphicsItemGroup on the Item to make it belong to a layer. The QGraphicsItemGroup bundles a bunch of Items in the same way as a QGraphicsScene and lets me apply parent Z values to the items so that layers stack. I give it a backreference to a StampLayerModel which contains the list I originally wanted. Thus I get a way to access and modify everything from everywhere by using the methods of the Stamp, the QGraphicsScene, and the StampLayerModel depending on the situation.

Implementing this sort of thing is the "ugliness" that I keep encountering in my code, basically:

Quote
class Stamp:
   """A single stamp placed onto a map."""
   def __init__(self,image="",x=0,y=0,rotation=0,xscale=1,yscale=1):
      self.image = image
      self.item = QtGui.QGraphicsPixmapItem(a.allimages[self.image],None, None)
      self.item.stamp = self
      rect = self.item.boundingRect()
      self.item.setOffset(-rect.width()/2,-rect.height()/2)
      self.setPos(x,y)
      self.setRotation(rotation)
      self.setScale(xscale,yscale)
   def setPos(self,x,y):
      self.x = x
      self.y = y
      self.item.setPos(self.x,self.y)
   def setRotation(self,rotation):
      self.rotation = rotation
      self.item.rotate(rotation)
   def setScale(self,xscale, yscale):
      self.xscale = xscale
      self.yscale = yscale
      self.item.scale(xscale,yscale)
   def json(self, stampdict):
      return [stampdict[self.image],self.x,self.y,self.rotation,self.xscale,self.yscale]

class StampLayerModel:
   """A layer of stamps."""
   def __init__(self, name="default"):
      self.d = []
      self.a = QtGui.QGraphicsItemGroup()
      self.a.model = self
      self.name = name
   def add_stamp(self, stamp):
      self.d.append(stamp)
      stamp.item.setGroup(self.a)
      stamp.item.setZValue(self.d.index(stamp))
   def remove_stamp(self, oldstampitem):
      idx = self.d.index(oldstampitem.stamp)
      oldstampitem.stamp.item = None
      oldstampitem.stamp = None
      del(self.d[idx])
      for n in range(len(self.d)):
         self.d[n].item.setZValue(n)
   def get_stamp(self, stampitem):
      return stampitem.stamp
   def replace_stamp(self, oldstampitem, newstamp):
      idx = self.d.index(oldstampitem.stamp)
      oldstampitem.setGroup(0)
      oldstampitem.stamp.item = None
      oldstampitem.stamp = None
      self.d[idx] = newstamp
      newstamp.item.setZValue(self.d.index(newstamp))

I haven't tested all the methods shown yet, but I can add stamps and position them OK. The "d" and "a" variable names are conventions used elsewhere to represent "data model" vs. "application settings+state". In most cases I'm dealing with static, app-wide variables, so I can correspondingly push them out to global containers with things like "a.map.brushnum" to get the selected brush of the map editor, but this case I have to maintain that distinction on a per-instance basis.
Logged

Triplefox
Level 9
****



View Profile WWW
« Reply #8 on: August 03, 2009, 04:50:54 PM »

I started a partial rewrite that solves all my problems by applying some formal structure that I was forgoing before. Even with that, I still plan to have a release ready this week. I'm also going to write up a tutorial based on this experience so that anyone interested in writing tools has a starting point.
Logged

Triplefox
Level 9
****



View Profile WWW
« Reply #9 on: August 23, 2009, 11:24:54 PM »

Well, it took me way longer than expected, and I slacked off on updating, but I've finally got something to release! It's not a packaged all-in-one binary, I'm afraid - it takes some effort to support that that I don't feel like going through at the moment. I'll be starting a thread in announcements as well.
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic