Giter Club home page Giter Club logo

hump's Introduction

hump - Helper Utilities for Massive Progression

hump is a small collection of tools for developing games with LÖVE. Build Status

Contents:

  • gamestate.lua: Easy gamestate management.
  • timer.lua: Delayed and time-limited function calls and tweening.
  • vector.lua: 2D vector math.
  • vector-light.lua: Lightweight 2D vector math (for optimisation purposes - leads to potentially ugly code).
  • class.lua: Lightweight object orientation (class or prototype based).
  • signal.lua: Simple Signal/Slot (aka. Observer) implementation.
  • camera.lua: Move-, zoom- and rotatable camera with camera locking and movement smoothing.

Documentation

You can find the documentation here: hump.readthedocs.org

License

Copyright (c) 2010-2018 Matthias Richter

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

hump's People

Contributors

alesegdia avatar am0d avatar clofresh avatar davidskeck avatar easternmouse avatar grunkgrunk avatar harris-m avatar hewills avatar itsmapleleaf avatar jessevanherk avatar jlaceda avatar julianwebb avatar karai17 avatar kneeko avatar mattitanskane avatar mifuyne avatar oniietzschan avatar palmettos avatar qwertystop avatar ricardozanini avatar superquadratic avatar tesselode avatar tobiasvl avatar usysrc avatar vrld avatar xpol avatar yonaba avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hump's Issues

register wheelmoved callback

the gamestate lib doesnt register the relatively new love.wheelmoved( x, y ) callback.
I would greatly appreciate it if you could update it so that it is included cuz i have no idea
how to implement it into the current code.
ty :)

Enhancement: register a function to every signal

I'd like to be able to register a function that will be called with every 'emit'. I'd use that to call 'print' with every signal to have a log of events. e.g:

Signal.register(function(...) print("signal emmited", ...) end)

New Callbacks

I think that the touch input callbacks should be added to the game state library.

Timer.do_for crashes when after is not supplied

after is marked as optional in the documentation but Timer.do_for crashes in update() if it's not supplied:

Error: timer.lua:48: attempt to call field 'after' (a nil value)
stack traceback:
    timer.lua:48: in function <timer.lua:36>

consider unit tests and ci support

Some components might be harder to automate than others, depends on how tightly coupled they are with LOVE engine and how much we can verify without using visual tests. But using spies/mocks with BDD we should be able to have decent coverage.

If you don't mind (or isn't available to add tests), I would like work on them when available, since I am using some of the components like camera/gamestate.

Gamestates now require an init function

This code works:

gamestate = require 'gamestate'

game = {}
function game:init() end
function game:draw()
  love.graphics.print('hello world')
end

gamestate.switch(game)
gamestate.registerEvents()

If you take out game.init, however, there is an error about no init function being found. From using previous versions of hump.gamestate, I assume this isn't supposed to be required.

I assume this was caused by ca7fa8a.

Timer:every doesn't take the timer "lateness" into consideration

Basically, Timer:every behaves as if it was creating an after timer on every timeout. Given that after fires not sooner than the given delay, it usually exhibits some "lateness". So, every timer accumulates that error with each timeout and e.g. 100 repetitions with 0.1 interval take almost 11 seconds to complete (running at 60 FPS) when they are expected to take 10 + dt seconds at worst. I can suggest knife.timer and cron.lua as some examples of a correct implementation.

No way to be notified after Gamestate.pop()

It would be useful to have an activate() callback function that is called after Gamestate.pop(). Otherwise, there is no way to figure out when a state gets activated (the enter() callback function is not called on the new state after Gamestate.pop()).

The use case is to use Löve Frames’ state system: the state needs to be changed when a new state is activated.

Hump Timer Not Canceling

I've done a

gameTimer = Timer.new()

and then inside of my lander class I did a

self.spawn = gameTimer:addPeriodic(1, function() addEnemy("soldier", self.x, self.y) end)

This works wonderfully and I get a new soldier unit spawned every second. The problem comes when I try to remove the task. In the same lander class I call a

gameTimer:cancel(self.spawn)

Nothing seems to happen. I don't get an error, but the task continues to fire off every second.

I did a print(self.spawn) just to make sure the handle was the same, and it told me the same table hex identifier each time.

Any ideas on what I am doing wrong? Could this be a bug in the Timer library?

[Docs] Classes example with spaceship has typo and seemingly invalid Lua code

You have a typo in your if statement in the Classes collision handler (line 1071 in the html):

if self.collision_handler[other.type])

Also, I thought this example below it was quite elegant, but when I tried to implement it it didn't seem to be valid code:

function Spaceship:collision_handler["Spaceship"](other, dx, dy)
    -- ...
end

I think you would have to do:

Spaceship.collision_handler["Spaceship"] = function(self, other, dx, dy)
    -- ...
end

which is certainly less elegant.

Canvas fails to render when camera attached

Hello,

I'm running into an issue where within a block of camera attach/detach calls, a canvas fails to render correctly. I'm hoping you can help indicate the issue with my test code or verify that this is a bug with the expected behavior of hump.camera:

local attachcam = true -- set to false to see expected behavior
local camera = require('hump.camera')(0,0)
local canvasWithCam = love.graphics.newCanvas(50, 50)
local canvasWithoutCam = love.graphics.newCanvas(50, 50)

function love.draw()        
    -- Works as expected
    love.graphics.setCanvas(canvasWithoutCam)    
    love.graphics.rectangle('fill', 0, 0, 50, 50)    
    love.graphics.setCanvas()  
    love.graphics.draw(canvasWithoutCam, 0, 0)

    -- if cam is attached, canvas fails to render
    if attachcam then camera:attach() end
    love.graphics.setCanvas(canvasWithCam)    
    love.graphics.rectangle('fill', 0, 0, 50, 50)    
    love.graphics.setCanvas()  
    love.graphics.draw(canvasWithCam, 100, 0)
    if attachcam then camera:detach() end
end

I do not have any viable workaround at this time, but if I develop one I will post my updated code. Thanks for your time! Please let me know if you need any further details.

feature request: Timer.yoyo function

I'd like to be easily able to let something fade in and out again.

Sadly something simple like this won't work:

function DrawTimer:yoyo(delay, func)
    self:do_for(delay, func, function() self:do_for(delay, function(dt) func(-dt) end) end)
end

local t = 0
DrawTimer:yoyo(2, function(dt)
    t = t + dt
    print(t)
    love.graphics.setColor( 255, 255, 255, 255 * t/2 )
    love.graphics.draw(card.img,50,450)
    love.graphics.setColor( 255, 255, 255, 255 )
end)

The after() part of do_for would execute some big gap after func has finished, thus leaving a time frame where nothing is drawn.

My personal sad fix for this looks like this:

function DrawTimer:yoyo(time, func)
    self:add(time-0.01,function() self:do_for(time, function(dt) func(-dt) end) end)
    self:do_for(time, func)
end

Maybe you could cook up something useful?

gamestate.current should not be a function

It's more convenient and better performance gamestate.current().world vs gamestate.current.world

You always know when state changes, so you can update current then. State doesn't change often so using cpu cycles there is better than every time it's accessed

Everywhere you're using stack[#stack] in source you can replace with GS.current for cleaner code and again performance

rockspecs ?

A luarock package would be welcome. I find myself often using this outside of Löve.

C stack overflow in gamestate.lua

I have been getting the follow stack trace from gamestate.lua seems like a bad tail call to me.

See attached image for stack trace.
lua-error

Documentation, First example for class:clone() lists wrong output

The example for function class:clone() shows

Class = require 'hump.class'

point = Class{ x = 0, y = 0 }

a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10    10'

b = point:clone()
print(b.x, b.y) --> prints '10    10'

but when I run it the output is

10 10
0 0

So the example should read either

Class = require 'hump.class'

point = Class{ x = 0, y = 0 }

a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10    10'

b = point:clone()
print(b.x, b.y) --> prints '0    0'

or

Class = require 'hump.class'

point = Class{ x = 0, y = 0 }

a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10    10'

b = a:clone()
print(b.x, b.y) --> prints '10    10'

Right?

class:clone() doesn't use the source's metatable, and so cloned instances don't inherit methods.

To demonstrate:

Class = require 'hump.class'

Person = Class{}

function Person:init (name, age)
    self.name = name
    self.age = age
end

function Person:trace()
    print (self.name .. " is " .. self.age)
end

bob = Person ("Bob Smith", 34)
bob2 = bob:clone()

bob:trace() -- works fine
bob2:trace() -- with no metatable, trace is nil

Would there be any consequences of simply changing class:clone to:

local function clone(other)
    return setmetatable(include({}, other), getmetatable(other))
end

Gamestates not resetting?

When I use a gamestate multiple times, it seems to not clear all the data and fully reset the state. Is this by design, or perhaps a limitation of Lua, or just an overlook?

Example: I have a gameplay state and in the game there are two players, each taking turns as th eplayer who moves first after each round. When a player leaves the game and then starts a game form the title state, the data that determines who plays first is not reset and sometimes the wrong player starts first which causes some issues with the AI.

I found that manually clearing some data before calling newgame() fixes this, but I was not expecting to have a state with leftover residue.

Add a vector:angleBetween() function

Having an angleBetween function would save me a few extra keystrokes when I start up my projects. Something like this:

function vector:angleBetween(self, other)
    return math.atan2(other.y - self.y, other.x - self.x)
end

Note that this is different from angleTo, as angleTo returns the directed angle from self to other, while angleBetween would return the angle of the difference vector connecting the two vectors

Callback Order Inconsistancy

So I was using the infamous gamestate library, and I stumbled across something odd;

The order that the callbacks are executed in don't seem to correlate with love's callbacks;

In love, the love.update is called before love.draw, whereas when gamestate has a foo:update and foo:draw, it calls foo:draw first, then foo:update.

This Inconsistancy threw me off, and if possible, it would be nice if reversed.

My current workaround is to use :init or :enter to set the variables before the library :draw's.

timer.addPeriodic doesn't return the good function

I'm not totally sure about this, but addPeriodic seems to return a function which will only last during the first cycle. After that, timer.add() is called again and the return value is not valid anymore. Thus timer.cancel doesn't work with periodics.

I couldn't work on a real fix (I encounter it during miniLD, so not much time), but I have an ugly workaround (separating periodics and non periodics) if you are interested.

hump/gamestate.lua:40: attempt to index local 'to'

Hi, I am using hump for the first time and have set up a simple program only using the gamestates module and I have this error:

Error
hump/gamestate.lua:40: attempt to index local 'to' (a boolean value)
Traceback
hump/gamestate.lua:40: in function 'switch'
main.lua:5: in function 'load'
[C]: in function 'xpcall'

My main.lua is as follows:

Gamestate = require("hump.gamestate")
Splash = require("splash")
function love.load()
Gamestate.switch(Splash)
Gamestate.registerEvents()
end

My splash.lua is as follows

Splash = {}
function Splash:init()
end
function Splash:enter()
end
function Splash:leave()
end
function Splash:update(dt)
end
function Splash:draw()
love.graphics.print("Test",10,10)
end

Any idea what is causing the issue?

Thanks

The way camera:attach is implemented makes supporting non-even dimension difficult

Most of the discussion is here: karai17/Simple-Tiled-Implementation#106

In short, given current implementation, we will always get non-integer translation when attaching camera on a game window with odd number as dimensions, and there is nothing we can do except to give gap between tiles (not really a great solution when you have hundreds of tilesets without margins)

https://github.com/vrld/hump/blob/master/camera.lua#L105-L112

Feature request: Ability to cancel specific timers

It's stupidly easy--I suppose all you need to do is add:

local function cancel(timer)
   functions[timer] = nil;
end

But it would be very useful--sometimes when I'm switching states I want to cancel some of the timers I have set up.

Thanks, and thanks for making Hump, it's a great library :)

Doc for Timer.do_for()

The Timer.do_for() writes: function do_for(delay, func, after). The later descriptions reference the 1st arg as delta.

refactoring the whole code of hump.gamestate in a cleaner way

Hello lovers, this is my first message for the community as i'm very new to LÖVE, and lua (but with a C background ;p)

i'm currently writing a framework for making games development easier a little bit like this one, and i'm currently working on the gamestate library. After a bit of thinking, i maybe found the solution to handle this in a clean way, and it is based on overriding the default love.run function .

Because the main loop of the game actually goes into that function, it becomes obvious than redefining it gives you total control on all the aspects of LÖVE, making it the ideal place to hande the gamestate running / transitionning / whatever.

I'm currently working on that idea and will give you my results, but what do you think about it ?

[hump.signal] stopping signal propagation

Would be great to have a way to stop the signal propagation:

function Registry:emit(s, ...)
  for f in pairs(self[s]) do
    if f(...) == false then return end
  end
end

so we could stop the signal using:

Signal.register('mousepressed', function(x, y)
  ...
  return false
end)

a bug about single-class inheritance

When you instantiate an object from subclass, the base class constructor is not called.

-- single class inheritance test
A = class {
	init = function(self)
		self.x = 100
		print("A.init")
	end,

	test = function(self)
		print("A.test: x = ", self.x)
	end
}

B = class {
	__includes = A,

	init = function(self)
		print("B.init")
	end
}

local b = B()
b:test()

Output:

B.init
A.test: x =     nil

camera:worldCoords() not working

I've tried calling camera:worldCoords() on it's own and through camera:mousepos() but I always get the same error.

lib/hump/vector.lua:60: Add: Wrong argument types (<vector> expected)

[hump.gamestates] löve 0.11 random quits on errors

Some people have been noticing this behavior when using hump.gamestates; instead of the default error handler being called, it will just quit the application; 0.11 renamed love.errhand to love.errorhandler, so that one string should be changed in gamestate.lua to work correctly with the current version.

https://github.com/vrld/hump/blob/master/gamestate.lua#L78

Edit: That may not be the whole story though: https://love2d.org/forums/viewtopic.php?f=4&t=85098

Seriously, thank you.

Hump has totally changed my life, as far as game development goes.

I'm fairly new to all this, and I wrote my first game without it — and boy, I wish I had it when I started.

Seriously, thank you for this fantastic piece of code.

Sorry for the GitHub spam. ✨🍰✨

lua rock

hello, while it's nice that hump has a rockspec, it's only available via the luarocks.org dev server.

i assume there is a way to configure loverocks to use a different server, but i think it would be more convenient for many users to simply have the rock available on the standard server.

Clarify GameState usage in documentation

First off, thank you for having worked on this! I've been using it in Ludum Dare with great success!

In the recently passed Ludum Dare, I ran into an issue where my game state classes would only initialize once. At the time, I decided to just stuff that initialization stuff into the enter function and leave it. Now that I have more time though, I was able to check the source code for GameState and saw that the init method gets set to nil after having it called. This unfortunately sets class methods to nil as well, which caused a bit of confusion for me and possibly others I imagine. Perhaps the documentation should say something about this explicitly, as it is modifying code outside of the library?

A simple example might explain my confusion.

GS = require 'hump.gamestate'
class = require 'hump.class'

local state1 = class({})
local count = 5

function state1:init()
  print('Init is called only once, despite me switching with a class and not an object')
end

function state1:enter()
  print('Enter is always called')
end

function state1:update()
  --switching with class value, not the object `self`
  count = count - 1
  if count <= 0 then
    love.event.quit()
  end
  GS.switch(state1)
end

Thoughts? If you'd like, I can make the edit. It looks like it's stored on the gh-pages branch, which seems simple enough. Thank you!

about camera

I'm learning love2d, and hump is great.
Just now I update it, It throw.
In camera.lua 34, smooth.smooth = {}, it should be camera.smooth = {}.
Oh... My pool English. Hope you can understand.

isvector breaks when two vectors are created from different vector instances

local Vector = require "libs.hump.vector"
local Vec2 = require "libs.something_else.third_party.hump.vector"

local vertex1 = Vector(50, 50)
local vertex2 = Vec2(100, 100)

local sum = vertex1 + vertex2 -- crash!

I am using a library that has an internal instance of Vector, and I am needing to do some math with a local copy of Vector. The problem is, when isvector checks if they are both vectors (they are!), it decides that they aren't due to them coming from different instances of Vector.

Perhaps a fix for this would be to check the data structure's integrity instead of a pointer to the Vector library?

Gamestate.leave/enter functions are called immediately inside update function

I was just working on a game where I have a call to gamestate.switch, and the leave function tries to clean up a repeating sound, but the update function starts it again. So the code execution is like this:

[states.game:update]
if weShouldSwitchStates then
  State.switch(states.menu)
    [states.game.leave]
    sound:stop()
end
...
sound:play()

[states.menu:update]
.... (sound is playing)

You could just tell me to not do something like that, but if my update function is complex and enters a lot of other classes it's going to be hard not to have conflicts.

I'd suggest waiting until right before the new update/draw functions are called before calling leave and enter. That way I can be sure that leave is going to clean up all the resources I'm using in the gamestate.

Error in Documentation: gamestate

The examples there use:

key == 'enter'

while they should read

key == 'return'

Please test your examples. It is often hard enough to integrate libraries, if the examples are wrong this can get frustrating.

vector:angleTo() returns wrong angle

e.g. vector(0,-1):angleTo(vector(0,1)) returns -1.57... instead of -3.14...

I think, something like atan2(self.y, self.x) - atan2(other.y, other.x) should produce the correct angle.

About self:calls() in the timer lib

I realize there was an issue about this a while ago, but I just keep scratching my head at this the more I work with it, and it's bit me in the ass more times than I'd prefer.

-- hump.camera:
local cam = Camera()
cam:moveto(1337, 1337) -- self call


-- hump.vector:
local vec = Vector(573, 573)
vec:normalize() -- self call


-- hump.class:
Person = Class{
  init = function(self, name) self.name = name end,
  speak = function(self) print('my name is', self.name) end,
}

local p = Person()
person:speak() -- self call (though completely necessary as per what the library does)


-- hump.timer:
local timer = Timer()
timer.update(dt) -- ...no self call.

I... can't say I understand why the timer is inconsistent with the usage of the rest of the libraries. Only now have I noticed that hump.signal seems the same way.

When something is required globally as a library, it is to receive no self as a standard. However, instances should be receiving self in their function calls because they're instances, no? They should be operating on a given self as per basic logic of OOP, not on an invisible floating self variable somewhere in space, right?

I'm guessing the main difference here is to create consistency between the usage of libraries and that of the one that instances them, but then you're creating inconsistencies not only with the other parts of the lib, but with common lua OOP paradigms themselves ...and in my brain. Code like this is ambiguous and misleading, when everything is created and documented as an instance, but it appears to be a global usage of hump.timer. Other than from the naming of the variable, one wouldn't be able to tell at first glance.

vector:dothing()
camera:dothing()
timer.dothing()

Gamestate.switch should halt current state cycle

Currently when you switch Gamestates, Gamestate.leave is called immediately but the previous state still runs through its update/draw cycle one last time instead of canceling and switching to the new state. This causes lots of frustrating bugs when trying to deconstruct a state using Gamestate.leave to nil out some data.

It would be nice if Gamestate.switch would stop all actions, call leave, and go to the next state without finishing the current update/draw cycle.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.